ngular Generic Forms Angular UP 2018
About mySelf • Experienced FE developer, specialised in B2C applications. • FE Team Lead @ market.co.uk • Weekends FE developer @ fashbash.co
Where it all begins…
Where it all begins…
Email Input x12
Reusable Generic Form Control Objective 1 Requirements: • Can fit for type text and password • Can Validate on required and pattern • Can show indication whether the control is valid or not • Can show error messages
Wrap our input inside component
Wrap our input inside component
Using the custom input
No Value Accessor error
Introducing the ControlValueAccessor interface export interface ControlValueAccessor { writeValue(obj: any): void; registerOnChange(fn: any): void; registerOnTouched(fn: any): void; setDisabledState?(isDisabled: boolean): void; }
Let’s try again now… <div class="group"> <input> </div>
Handle Validations validate(c: AbstractControl): { [key: string]: any; } { const validators: ValidatorFn[] = []; if (this.isRequired) { validators.push(Validators.required); } if (this.pattern) { validators.push(Validators.pattern(this.pattern)); } return validators; }
Add Providers @Component({ selector: 'app-generic-input', templateUrl: './generic-input.component.html', styleUrls: ['./generic-input.component.scss'], providers: [{ provide: NG_VALUE_ACCESSOR, multi: true, useExisting: GenericInputComponent }, { provide: NG_VALIDATORS, multi: true, useExisting: GenericInputComponent }] })
Inject the NgControl Validations • Injecting FormControlName ? NgModel ? • NgControl is the super class of all the forms directives • @Self decorator? Avoid providing the wrong instance. constructor(@Self() public controlDir: NgControl) { this.controlDir.valueAccessor = this; }
Remove the providers Validations @Component({ selector: 'app-generic-input', templateUrl: './generic-input.component.html', styleUrls: ['./generic-input.component.scss'], // providers: [{ // provide: NG_VALUE_ACCESSOR, // multi: true, // useExisting: GenericInputComponent // }, // { // provide: NG_VALIDATORS, // multi: true, // useExisting: GenericInputComponent // }] })
Considering base class validators Validations ngOnInit() { const control = this.controlDir.control; const validators: ValidatorFn[] = control.validator ? [control.validator] : []; if (this.isRequired) { validators.push(Validators.required); } if (this.pattern) { validators.push(Validators.pattern(this.pattern)); } control.setValidators(validators); control.updateValueAndValidity(); }
Show the error on the template Validations
And…THAT’ IT!
Now we got this…
Reusable Form Objective 2 Requirements: • Create an Address form which can be reused. • Implement the form - UI, controls, Validations - just once.
Implementing the controlValueAccessor Reusable form
But..Maybe there is a quicker way to do it • Start from add controlContainer to you viewProviders as FormGroupDirective @Component({ selector: 'app-address-form', templateUrl: './address-form.component.html', styleUrls: ['./address-form.component.scss'], viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }] }) • Why we using viewProviders and not the usual providers?
Let’s look at the FormGroupName directive source @Host() - Specifies that an injector should retrieve a dependency from any injector until reaching the host element of the current component
Reusable form • Then - inject the FormGroupDirective and set your local form to the container form export class AddressFormComponent implements OnInit { @Input() address: Address; form: FormGroup; constructor( private ctrlContainer: FormGroupDirective, private formBuilder: FormBuilder) { } ngOnInit() { this.form = this.ctrlContainer.form;
Reusable form • Now you can setup your form and validations this.form.addControl('addressForm', this.formBuilder.group({ 'firstName': [null, [Validators.required]], 'lastName': [null, [Validators.required]], 'phone': [null, null], 'street': [null, [Validators.required]], 'city': [null, [Validators.required]], 'state': [null], 'zip': [null, [Validators.required]], }));
Reusable form • Now you can use your addressForm inside any reactive formGroup and get it as control <form [formGroup]="addressForm" novalidate (ngSubmit)="onSubmit()"> <app-address-form [address]="address"></app-address-form> <button class="btn btn-primary" type="submit" [disabled]="! addressForm.valid"> Submit </button> </form>
And…THAT’S IT!
ng-forms-creator
ng-form-creator
ng-form-creator
Summary • Create custom form control by implementing the controlValueAccessor • Create custom reusable forms • Create generic input and control it by inputs • ng-forms-creator package - will be released in the next few weeks as an open source.
Angular genericforms2

Angular genericforms2

  • 1.
  • 2.
    About mySelf • ExperiencedFE developer, specialised in B2C applications. • FE Team Lead @ market.co.uk • Weekends FE developer @ fashbash.co
  • 3.
    Where it allbegins…
  • 4.
    Where it allbegins…
  • 6.
  • 8.
    Reusable Generic FormControl Objective 1 Requirements: • Can fit for type text and password • Can Validate on required and pattern • Can show indication whether the control is valid or not • Can show error messages
  • 9.
    Wrap our inputinside component
  • 10.
    Wrap our inputinside component
  • 11.
  • 12.
  • 14.
    Introducing the ControlValueAccessorinterface export interface ControlValueAccessor { writeValue(obj: any): void; registerOnChange(fn: any): void; registerOnTouched(fn: any): void; setDisabledState?(isDisabled: boolean): void; }
  • 15.
    Let’s try againnow… <div class="group"> <input> </div>
  • 17.
    Handle Validations validate(c: AbstractControl):{ [key: string]: any; } { const validators: ValidatorFn[] = []; if (this.isRequired) { validators.push(Validators.required); } if (this.pattern) { validators.push(Validators.pattern(this.pattern)); } return validators; }
  • 18.
    Add Providers @Component({ selector: 'app-generic-input', templateUrl:'./generic-input.component.html', styleUrls: ['./generic-input.component.scss'], providers: [{ provide: NG_VALUE_ACCESSOR, multi: true, useExisting: GenericInputComponent }, { provide: NG_VALIDATORS, multi: true, useExisting: GenericInputComponent }] })
  • 19.
    Inject the NgControl Validations •Injecting FormControlName ? NgModel ? • NgControl is the super class of all the forms directives • @Self decorator? Avoid providing the wrong instance. constructor(@Self() public controlDir: NgControl) { this.controlDir.valueAccessor = this; }
  • 20.
    Remove the providers Validations @Component({ selector:'app-generic-input', templateUrl: './generic-input.component.html', styleUrls: ['./generic-input.component.scss'], // providers: [{ // provide: NG_VALUE_ACCESSOR, // multi: true, // useExisting: GenericInputComponent // }, // { // provide: NG_VALIDATORS, // multi: true, // useExisting: GenericInputComponent // }] })
  • 21.
    Considering base classvalidators Validations ngOnInit() { const control = this.controlDir.control; const validators: ValidatorFn[] = control.validator ? [control.validator] : []; if (this.isRequired) { validators.push(Validators.required); } if (this.pattern) { validators.push(Validators.pattern(this.pattern)); } control.setValidators(validators); control.updateValueAndValidity(); }
  • 22.
    Show the erroron the template Validations
  • 23.
  • 24.
    Now we gotthis…
  • 25.
    Reusable Form Objective 2 Requirements: •Create an Address form which can be reused. • Implement the form - UI, controls, Validations - just once.
  • 26.
  • 27.
    But..Maybe there isa quicker way to do it • Start from add controlContainer to you viewProviders as FormGroupDirective @Component({ selector: 'app-address-form', templateUrl: './address-form.component.html', styleUrls: ['./address-form.component.scss'], viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }] }) • Why we using viewProviders and not the usual providers?
  • 28.
    Let’s look atthe FormGroupName directive source @Host() - Specifies that an injector should retrieve a dependency from any injector until reaching the host element of the current component
  • 29.
    Reusable form • Then- inject the FormGroupDirective and set your local form to the container form export class AddressFormComponent implements OnInit { @Input() address: Address; form: FormGroup; constructor( private ctrlContainer: FormGroupDirective, private formBuilder: FormBuilder) { } ngOnInit() { this.form = this.ctrlContainer.form;
  • 30.
    Reusable form • Nowyou can setup your form and validations this.form.addControl('addressForm', this.formBuilder.group({ 'firstName': [null, [Validators.required]], 'lastName': [null, [Validators.required]], 'phone': [null, null], 'street': [null, [Validators.required]], 'city': [null, [Validators.required]], 'state': [null], 'zip': [null, [Validators.required]], }));
  • 31.
    Reusable form • Nowyou can use your addressForm inside any reactive formGroup and get it as control <form [formGroup]="addressForm" novalidate (ngSubmit)="onSubmit()"> <app-address-form [address]="address"></app-address-form> <button class="btn btn-primary" type="submit" [disabled]="! addressForm.valid"> Submit </button> </form>
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
    Summary • Create customform control by implementing the controlValueAccessor • Create custom reusable forms • Create generic input and control it by inputs • ng-forms-creator package - will be released in the next few weeks as an open source.