Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dynamic-forms): allow elements to be disabled via configuration #1219

Merged
merged 10 commits into from
Aug 15, 2018
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,28 @@
<h4>Form elements</h4>
<ng-template let-model let-last="last" let-index="index" ngFor [ngForOf]="dynamicElements">
<td-expansion-panel [label]="model.name">
<mat-divider></mat-divider>
<mat-divider [inset]="true"></mat-divider>
<div class="pad" layout="column">
<div layout="row">
<mat-form-field *ngIf="!isMinMaxSupported(model.type)"class="pad-right-xs" flex>
<input matInput
placeholder="Default"
[(ngModel)]="model.default"
name="default">
</mat-form-field>
<mat-form-field *ngIf="!isDate(model.type) && isMinMaxSupported(model.type)" class="pad-right-xs" flex>
<input matInput type="number" placeholder="Default" [(ngModel)]="model.default" name="default">
</mat-form-field>
<mat-form-field *ngIf="isDate(model.type) && isMinMaxSupported(model.type)" class="pad-right-xs" flex>
<input matInput
[matDatepicker]="defaultDatepicker"
placeholder="Default"
[(ngModel)]="model.default"
name="default">
<mat-datepicker-toggle matSuffix [for]="defaultDatepicker"></mat-datepicker-toggle>
<mat-datepicker #defaultDatepicker></mat-datepicker>
</mat-form-field>
</div>
<div layout="row">
<mat-form-field class="pad-right-xs" flex>
<input matInput
Expand Down Expand Up @@ -134,6 +154,7 @@ <h4>Form elements</h4>
</div>
<div layout="row">
<mat-slide-toggle [(ngModel)]="model.required" name="required">Required</mat-slide-toggle>
<mat-slide-toggle [(ngModel)]="model.disabled" name="disabled">Disabled</mat-slide-toggle>
<span flex></span>
<button mat-icon-button matTooltip="Delete element" (click)="deleteElement(index)">
<mat-icon>delete</mat-icon>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export class DynamicFormsDemoComponent {
required: false,
default: 'Default',
flex: 50,
disabled: true,
}, {
name: 'textarea',
hint: 'this is a textarea hint',
Expand Down
2 changes: 2 additions & 0 deletions src/platform/dynamic-forms/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export interface ITdDynamicElementConfig {
hint?: string;
type: TdDynamicType | TdDynamicElement | Type<any>;
required?: boolean;
disabled?: boolean;
min?: any;
max?: any;
minLength?: any;
Expand Down Expand Up @@ -139,6 +140,7 @@ export class Demo {
name: 'boolean',
type: TdDynamicType.Boolean,
default: false,
disabled: true,
}, {
name: 'select',
type: TdDynamicElement.Select,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
<mat-form-field tdFileDrop
class="td-dynamic-file-input-field"
floatLabel="never"
[disabled]="control?.disabled"
(fileDrop)="_handlefileDrop($event)"
(click)="fileInput.inputElement.click()"
(keyup.enter)="fileInput.inputElement.click()"
(click)="!control?.disabled && fileInput.inputElement.click()"
(keyup.enter)="!control?.disabled && fileInput.inputElement.click()"
(keyup.delete)="fileInput.clear()"
(keyup.backspace)="fileInput.clear()">
<input matInput
[value]="control?.value?.name"
[placeholder]="label"
[disabled]="control?.disabled"
readonly/>
<mat-hint>{{hint}}</mat-hint>
<mat-error>
Expand All @@ -22,7 +24,9 @@
<button mat-icon-button *ngIf="control.value" (click)="fileInput.clear()" (keyup.enter)="fileInput.clear()">
<mat-icon>cancel</mat-icon>
</button>
<td-file-input class="td-file-input" #fileInput [formControl]="control">
<td-file-input class="td-file-input"
#fileInput
[formControl]="control">
<mat-icon>folder</mat-icon>
<span>{{ label }}</span>
</td-file-input>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
<div class="td-dynamic-slider-wrapper">
<div class="mat-form-field-placeholder-wrapper mat-form-field-can-float mat-form-field-should-float"
[class.mat-focused]="slider._isActive">
<label class="mat-form-field-placeholder mat-float mat-form-field-float td-slider-label"> {{label}} <span *ngIf="required" class="mat-placeholder-required">*</span></label>
</div>
<div class="td-dynamic-slider-wrapper mat-form-field mat-form-field-can-float mat-form-field-should-float"
[class.mat-focused]="slider._isActive">
<span class="mat-form-field-label-wrapper">
<label class="mat-form-field-label mat-primary td-slider-label">
{{label}}
<span *ngIf="required && !control?.disabled" class="mat-form-field-required-marker">*</span>
</label>
</span>
<div class="td-dynamic-slider-field">
<mat-slider #slider
class="td-dynamic-slider"
[formControl]="control"
class="td-dynamic-slider"
[formControl]="control"
[min]="min"
[max]="max"
thumbLabel
tickInterval="auto"
[required]="required">
[required]="required"
(blur)="_handleBlur()">
</mat-slider>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
:host {
.td-dynamic-slider-wrapper {
display: block;
}
}
.td-dynamic-slider-field {
position: relative;
margin-top: 8px;
// [layout="row"]
flex-direction: row;
// [layout]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { Component, ChangeDetectorRef } from '@angular/core';
import { FormControl } from '@angular/forms';

@Component({
Expand All @@ -18,4 +18,11 @@ export class TdDynamicSliderComponent {

max: number = undefined;

constructor(private _changeDetectorRef: ChangeDetectorRef) {}

_handleBlur(): void {
setTimeout(() => {
this._changeDetectorRef.markForCheck();
});
}
}
5 changes: 5 additions & 0 deletions src/platform/dynamic-forms/dynamic-forms.component.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
.td-dynamic-form-wrapper {
::ng-deep {
.mat-form-field-infix {
width: auto;
}
}
// [layout-wrap]
flex-wrap: wrap;
// [layout]
Expand Down
32 changes: 29 additions & 3 deletions src/platform/dynamic-forms/dynamic-forms.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ describe('Component: TdDynamicForms', () => {
fixture.debugElement.query(By.directive(TdDynamicFormsComponent)).componentInstance;
expect(dynamicFormsComponent.valid).toBeFalsy();
/* tslint:disable-next-line */
expect(JSON.stringify(dynamicFormsComponent.value)).toBe(JSON.stringify({first_name: null, on_it: true}));
expect(JSON.stringify(dynamicFormsComponent.value)).toBe(JSON.stringify({on_it: true}));
});
})));

Expand All @@ -127,7 +127,7 @@ describe('Component: TdDynamicForms', () => {
fixture.debugElement.query(By.directive(TdDynamicFormsComponent)).componentInstance;
expect(dynamicFormsComponent.valid).toBeFalsy();
/* tslint:disable-next-line */
expect(JSON.stringify(dynamicFormsComponent.value)).toBe(JSON.stringify({first_name: null, age: 17}));
expect(JSON.stringify(dynamicFormsComponent.value)).toBe(JSON.stringify({age: 17}));
});
})));

Expand Down Expand Up @@ -319,8 +319,34 @@ describe('Component: TdDynamicForms', () => {
});
})));

it('should render dynamic custom element', async(inject([], () => {
it('should render dynamic elements with one element disabled', async(inject([], () => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TdDynamicFormsTestComponent);
let component: TdDynamicFormsTestComponent = fixture.debugElement.componentInstance;

expect(fixture.debugElement.queryAll(By.directive(TdDynamicElementComponent)).length).toBe(0);
component.elements = [{
name: 'hexColor',
type: TdDynamicType.Text,
required: true,
default: '#F1F1F1',
}, {
name: 'number',
type: TdDynamicType.Number,
disabled: true,
required: true,
}];
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.queryAll(By.directive(TdDynamicElementComponent)).length).toBe(2);
let dynamicFormsComponent: TdDynamicFormsComponent =
fixture.debugElement.query(By.directive(TdDynamicFormsComponent)).componentInstance;
expect(dynamicFormsComponent.valid).toBeTruthy();
/* tslint:disable-next-line */
expect(JSON.stringify(dynamicFormsComponent.value)).toBe(JSON.stringify({hexColor: '#F1F1F1'}));
});
})));

it('should render dynamic custom element', async(inject([], () => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TdDynamicFormsTestComponent);
let component: TdDynamicFormsTestComponent = fixture.debugElement.componentInstance;

Expand Down
13 changes: 11 additions & 2 deletions src/platform/dynamic-forms/dynamic-forms.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,19 @@ export class TdDynamicFormsComponent implements AfterContentInit {
throw new Error(`Dynamic element name: "${elem.name}" is duplicated`);
}
duplicates.push(elem.name);
if (!this.dynamicForm.get(elem.name)) {
let dynamicElement: AbstractControl = this.dynamicForm.get(elem.name);
if (!dynamicElement) {
this.dynamicForm.addControl(elem.name, this._dynamicFormsService.createFormControl(elem));
} else {
this.dynamicForm.get(elem.name).setValidators(this._dynamicFormsService.createValidators(elem));
dynamicElement.setValue(elem.default);
dynamicElement.markAsPristine();
dynamicElement.markAsUntouched();
if (elem.disabled) {
dynamicElement.disable();
} else {
dynamicElement.enable();
}
dynamicElement.setValidators(this._dynamicFormsService.createValidators(elem));
}
// copy objects so they are only changes when calling this method
this._renderedElements.push(Object.assign({}, elem));
Expand Down
3 changes: 2 additions & 1 deletion src/platform/dynamic-forms/services/dynamic-forms.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface ITdDynamicElementConfig {
hint?: string;
type: TdDynamicType | TdDynamicElement | Type<any>;
required?: boolean;
disabled?: boolean;
min?: any;
max?: any;
minLength?: any;
Expand Down Expand Up @@ -104,7 +105,7 @@ export class TdDynamicFormsService {
*/
createFormControl(config: ITdDynamicElementConfig): FormControl {
let validator: ValidatorFn = this.createValidators(config);
return new FormControl(config.default, validator);
return new FormControl({ value: config.default, disabled: config.disabled }, validator);
}

/**
Expand Down