Skip to content

Commit

Permalink
feat(dynamic-forms): add minLength and maxLength validations (#862)
Browse files Browse the repository at this point in the history
* feat(dynamic-forms): 577 add minLength and maxLength validations

closes #577

* fix(): reorder elements on demo so they fit properly

* docs(dynamic forms): #577 document api additions

closes #577
  • Loading branch information
jerryorta-dev authored and emoralesb05 committed Sep 8, 2017
1 parent 6906c75 commit be37ea1
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
<span *ngIf="control.hasError('required')">Required</span>
<span *ngIf="control.hasError('min')">Min value: {{element.min}}</span>
<span *ngIf="control.hasError('max')">Max value: {{element.max}}</span>
<span *ngIf="control.hasError('minlength')">MinLength value: {{element.minLength}}</span>
<span *ngIf="control.hasError('maxlength')">MaxLength value: {{element.maxLength}}</span>
</span>
</ng-template>
</ng-template>
Expand Down Expand Up @@ -101,6 +103,22 @@ <h4>Form elements</h4>
name="max">
</md-form-field>
</div>
<div *ngIf="isMinMaxLengthSupported(model.type)" layout="row">
<md-form-field class="pad-right-xs" flex>
<input mdInput
type="text"
placeholder="MinLength"
[(ngModel)]="model.minLength"
name="min">
</md-form-field>
<md-form-field flex="50">
<input mdInput
type="text"
placeholder="MaxLength"
[(ngModel)]="model.maxLength"
name="max">
</md-form-field>
</div>
<div layout="row">
<md-slide-toggle [(ngModel)]="model.required" name="required">Required</md-slide-toggle>
<span flex></span>
Expand All @@ -127,6 +145,14 @@ <h3 class="md-title">Dynamic Text Elements</h3>
<md-tab>
<ng-template mdTabLabel>Demo</ng-template>
<td-dynamic-forms [elements]="textElements">
<ng-template let-element ngFor [ngForOf]="textElements">
<ng-template let-control="control" [tdDynamicFormsError]="element.name">
<span *ngIf="control.touched || !control.pristine">
<span *ngIf="control.hasError('minlength')">Min length value: {{element.minLength}}</span>
<span *ngIf="control.hasError('maxlength')">Max length value: {{element.minLength}}</span>
</span>
</ng-template>
</ng-template>
</td-dynamic-forms>
</md-tab>
<md-tab>
Expand All @@ -135,6 +161,14 @@ <h3 class="md-title">Dynamic Text Elements</h3>
<td-highlight lang="html">
<![CDATA[
<td-dynamic-forms [elements]="textElements">
<ng-template let-element ngFor [ngForOf]="textElements">
<ng-template let-control="control" [tdDynamicFormsError]="element.name">
<span *ngIf="control.touched || !control.pristine">
<span *ngIf="control.hasError('minlength')">Min length value: { {element.minLength} }</span>
<span *ngIf="control.hasError('maxlength')">Max length value: { {element.minLength} }</span>
</span>
</ng-template>
</ng-template>
</td-dynamic-forms>
]]>
</td-highlight>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Component, HostBinding, ViewChild, ViewRef, AfterViewInit } from '@angular/core';
import { AbstractControl, ValidatorFn, FormGroup, Validators } from '@angular/forms';
import { Component, HostBinding } from '@angular/core';
import { AbstractControl, Validators } from '@angular/forms';
import { slideInDownAnimation } from '../../../app.animations';

import { TdDynamicType, ITdDynamicElementConfig,
TdDynamicElement, ITdDynamicElementValidator, TdDynamicFormsComponent } from '@covalent/dynamic-forms';
import {
ITdDynamicElementConfig,
ITdDynamicElementValidator,
TdDynamicElement,
TdDynamicFormsComponent,
TdDynamicType,
} from '@covalent/dynamic-forms';

@Component({
selector: 'dynamic-forms-demo',
Expand All @@ -28,15 +33,22 @@ export class DynamicFormsDemoComponent {
required: true,
flex: 50,
}, {
name: 'textarea',
type: TdDynamicElement.Textarea,
required: false,
name: 'text-length',
label: 'Text Length',
type: TdDynamicElement.Input,
minLength: 4,
maxLength: 12,
flex: 50,
}, {
name: 'text',
type: TdDynamicType.Text,
required: false,
default: 'Default',
flex: 100,
flex: 50,
}, {
name: 'textarea',
type: TdDynamicElement.Textarea,
required: false,
}, {
name: 'required-password',
label: 'Password Label',
Expand Down Expand Up @@ -190,6 +202,10 @@ export class DynamicFormsDemoComponent {
return type === TdDynamicElement.Slider || type === TdDynamicType.Number;
}

isMinMaxLengthSupported(type: TdDynamicElement | TdDynamicType): boolean {
return type === TdDynamicElement.Input || type === TdDynamicType.Text;
}

addElement(): void {
if (this.type) {
this.dynamicElements.push({
Expand Down
10 changes: 10 additions & 0 deletions src/platform/dynamic-forms/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export interface ITdDynamicElementConfig {
required?: boolean;
min?: any;
max?: any;
minLength?: string;
maxLength?: string;
selections?: any[];
default?: any;
validators?: ITdDynamicElementValidator[];
Expand All @@ -61,6 +63,8 @@ Example for HTML usage:
<span *ngIf="control.hasError('required')">Required</span>
<span *ngIf="control.hasError('min')">Min value: {{element.min}}</span>
<span *ngIf="control.hasError('max')">Max value: {{element.max}}</span>
<span *ngIf="control.hasError('minlength')">Min length value: {{element.minLength}}</span>
<span *ngIf="control.hasError('maxlength')">Max length value: {{element.minLength}}</span>
</span>
</ng-template>
</ng-template>
Expand All @@ -76,6 +80,12 @@ export class Demo {
name: 'input',
type: TdDynamicElement.Input,
required: true,
}, {
name: 'textLength',
label: 'Text Length',
type: TdDynamicElement.Input,
minLength: 4,
maxLength: 12,
}, {
name: 'number',
type: TdDynamicType.Number,
Expand Down
12 changes: 12 additions & 0 deletions src/platform/dynamic-forms/dynamic-element.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ export class TdDynamicElementComponent extends AbstractControlValueAccessor
*/
@Input() max: number = undefined;

/**
* Sets minLength validation checkup (if supported by element).
*/
@Input() minLength: number = undefined;

/**
* Sets maxLength validation checkup (if supported by element).
*/
@Input() maxLength: number = undefined;

/**
* Sets selections for array elements (if supported by element).
*/
Expand Down Expand Up @@ -120,6 +130,8 @@ export class TdDynamicElementComponent extends AbstractControlValueAccessor
this._instance.required = this.required;
this._instance.min = this.min;
this._instance.max = this.max;
this._instance.minLength = this.minLength;
this._instance.maxLength = this.maxLength;
this._instance.selections = this.selections;
this._instance.registerOnChange((value: any) => {
this.value = value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
[required]="required"
[attr.min]="min"
[attr.max]="max"
[attr.minLength]="minLength"
[attr.maxLength]="maxLength"
flex/>
</md-form-field>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@ export class TdDynamicInputComponent extends AbstractControlValueAccessor implem

max: number = undefined;

minLength: number = undefined;

maxLength: number = undefined;

}
2 changes: 2 additions & 0 deletions src/platform/dynamic-forms/dynamic-forms.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
[required]="element.required"
[min]="element.min"
[max]="element.max"
[minLength]="element.minLength"
[maxLength]="element.maxLength"
[selections]="element.selections">
</td-dynamic-element>
<div class="tc-red-600 md-caption text-sm"
Expand Down
70 changes: 70 additions & 0 deletions src/platform/dynamic-forms/dynamic-forms.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,76 @@ describe('Component: TdDynamicForms', () => {
});
})));

it('should render dynamic elements and show form invalid because character length is less than minLength', 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: 'password',
type: TdDynamicType.Text,
minLength: 8,
default: 'mypwd',
}];
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.queryAll(By.directive(TdDynamicElementComponent)).length).toBe(1);
let dynamicFormsComponent: TdDynamicFormsComponent =
fixture.debugElement.query(By.directive(TdDynamicFormsComponent)).componentInstance;
expect(dynamicFormsComponent.valid).toBeFalsy();
/* tslint:disable-next-line */
expect(JSON.stringify(dynamicFormsComponent.value)).toBe(JSON.stringify({password: 'mypwd'}));
});
})));

it('should render dynamic elements and show form invalid because character length is more than maxLength', 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: 'password',
type: TdDynamicType.Text,
maxLength: 8,
default: 'myVeryLongString',
}];
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.queryAll(By.directive(TdDynamicElementComponent)).length).toBe(1);
let dynamicFormsComponent: TdDynamicFormsComponent =
fixture.debugElement.query(By.directive(TdDynamicFormsComponent)).componentInstance;
expect(dynamicFormsComponent.valid).toBeFalsy();
/* tslint:disable-next-line */
expect(JSON.stringify(dynamicFormsComponent.value)).toBe(JSON.stringify({password: 'myVeryLongString'}));
});
})));

it('should render dynamic elements and show form valid', 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: 'password',
type: TdDynamicType.Text,
minLength: 8,
maxLength: 20,
default: 'mySuperSecretPw',
}];
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.queryAll(By.directive(TdDynamicElementComponent)).length).toBe(1);
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({password: 'mySuperSecretPw'}));
});
})));

it('should render dynamic elements and show form invalid with custom validation', async(inject([], () => {

let fixture: ComponentFixture<any> = TestBed.createComponent(TdDynamicFormsTestComponent);
Expand Down
8 changes: 8 additions & 0 deletions src/platform/dynamic-forms/services/dynamic-forms.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export interface ITdDynamicElementConfig {
required?: boolean;
min?: any;
max?: any;
minLength?: any;
maxLength?: any;
selections?: string[] | { value: any, label: string }[];
default?: any;
flex?: number;
Expand Down Expand Up @@ -111,6 +113,12 @@ export class TdDynamicFormsService {
if (config.min || config.min === 0) {
validator = Validators.compose([validator, Validators.min(parseFloat(config.min))]);
}
if (config.maxLength || config.maxLength === 0) {
validator = Validators.compose([validator, Validators.maxLength(parseFloat(config.maxLength))]);
}
if (config.minLength || config.minLength === 0) {
validator = Validators.compose([validator, Validators.minLength(parseFloat(config.minLength))]);
}
// Add provided custom validators to the validator function
if (config.validators) {
config.validators.forEach((validatorConfig: ITdDynamicElementValidator) => {
Expand Down

0 comments on commit be37ea1

Please sign in to comment.