Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheriting validation using ControlValueAccessor in Angular

I have a custom form control component (it is a glorified input). The reason for it being a custom component is for ease of UI changes - i.e. if we change the way we style our input controls fundamentally it will be easy to propagate change across the whole application.

Currently we are using Material Design in Angular https://material.angular.io

which styles controls very nicely when they are invalid.

We have implemented ControlValueAccessor in order to allow us to pass a formControlName to our custom component, which works perfectly; the form is valid/invalid when the custom control is valid/invalid and the application functions as expected.

However, the issue is that we need to style the UI inside the custom component based on whether it is invalid or not, which we don't seem to be able to do - the input that actually needs to be styled is never validated, it simply passes data to and from the parent component.

COMPONENT.ts

import { Component, forwardRef, Input, OnInit } from '@angular/core'; import {     AbstractControl,     ControlValueAccessor,     NG_VALIDATORS,     NG_VALUE_ACCESSOR,     ValidationErrors,     Validator, } from '@angular/forms';  @Component({   selector: 'app-input',   templateUrl: './input.component.html',   styleUrls: ['./input.component.css'],   providers: [     {       provide: NG_VALUE_ACCESSOR,       useExisting: forwardRef(() => InputComponent),       multi: true     }   ] }) export class InputComponent implements OnInit, ControlValueAccessor {   writeValue(obj: any): void {     this._value = obj;   }   registerOnChange(fn: any): void {     this.onChanged = fn;   }   registerOnTouched(fn: any): void {     this.onTouched = fn;   }   setDisabledState?(isDisabled: boolean): void {     this.disabled = isDisabled;   }    get value() {     return this._value;   }    set value(value: any) {     if (this._value !== value) {       this._value = value;       this.onChanged(value);     }   }    @Input() type: string;    onBlur() {     this.onTouched();   }    private onTouched = () => {};   private onChanged = (_: any) => {};   disabled: boolean;    private _value: any;    constructor() { }    ngOnInit() {   }  } 

COMPONENT.html

<ng-container [ngSwitch]="type">   <md-input-container class="full-width" *ngSwitchCase="'text'">     <span mdPrefix><md-icon>lock_outline</md-icon> &nbsp; </span>     <input mdInput placeholder="Password" type="text" [(ngModel)]="value" (blur)="onBlur()" />   </md-input-container> </ng-container> 

example use on page:

HTML:

<app-input type="text" formControlName="foo"></app-input> 

TS:

this.form = this.fb.group({         foo: [null, Validators.required]     }); 
like image 531
matthall74 Avatar asked Aug 07 '17 22:08

matthall74


People also ask

What is ControlValueAccessor in Angular?

Control Value Accessor is an interface that provides us the power to leverage the Angular forms API and create a communication between Angular Form API and the DOM element. It provides us many facilities in angular like we can create custom controls or custom component with the help of control value accessor interface.

What is Ng_value_accessor?

NG_VALUE_ACCESSOR provider specifies a class that implements ControlValueAccessor interface and is used by Angular to setup synchronization with formControl . It's usually the class of the component or directive that registers the provider.

What is custom validation in Angular?

A validator in Angular is a function which returns null if a control is valid or an error object if it's invalid. For model-driven forms we create custom validation functions and pass them into the FormControl constructor.


1 Answers

You can get access of the NgControl through DI. NgControl has all the information about validation status. To retrieve NgControl you should not provide your component through NG_VALUE_ACCESSOR instead you should set the accessor in the constructor.

@Component({   selector: 'custom-form-comp',   templateUrl: '..',   styleUrls: ... }) export class CustomComponent implements ControlValueAccessor {     constructor(@Self() @Optional() private control: NgControl) {      this.control.valueAccessor = this;    }     // ControlValueAccessor methods and others     public get invalid(): boolean {      return this.control ? this.control.invalid : false;    }     public get showError(): boolean {       if (!this.control) {        return false;       }        const { dirty, touched } = this.control;        return this.invalid ? (dirty || touched) : false;    } } 

Please go through this article to know the complete information.

like image 66
VJAI Avatar answered Oct 09 '22 15:10

VJAI