Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access valid value of custom form control

I created custom component representing password form control (code below is simplified).

PasswordComponent (html)

<form [formGroup]="passwordForm">
  ...
  <input formControlName="password" type="password">
</form>

PasswordComponent (ts)

...
@Component({
  selector: 'password',
  templateUrl: './password.component.html',
  styleUrls: ['./password.component.css'],
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => PasswordComponent),
    multi: true
  }]
})
export class PasswordComponent implements ControlValueAccessor {

  passwordForm: FormGroup;
  onChange = (password: string) => { };  
  onTouched = () => { };                  

  constructor() {
    this.passwordForm = new FormGroup({
      ...
      password: new FormControl('')
    });

    this.passwordForm.valueChanges.subscribe(data => this.onChange(this.value));
  }

  get value(): string {
    return this.passwordForm.get('password').value;
  }

  writeValue(password: string): void {
    this.passwordForm.get('password').setValue(password);
    this.onChange(this.value);
  }

  registerOnChange(fn: any): void { this.onChange = fn; } 

  registerOnTouched(fn: any): void { this.onTouched = fn; }

  setDisabledState?(isDisabled: boolean): void { }
}

I use it in other components instead of standard input element:

<form [formGroup]="userForm">
  ...
  <password formControlName="password"></password>
</form>

Validators are coming from outer form (they're not defined inside PasswordComponent)

this.userForm = fb.group({
  ...
  password: ['', [Validators.minLength(10), Validators.maxLength(100)]] 
});

My question is: how can I get <password> element validity from inside PasswordComponent? I would like to stylize it based on validity. In other words how can I get validity of userForm's 'password' control from PasswordComponent that represents this control.

like image 928
Wilhelm Olejnik Avatar asked Aug 06 '17 20:08

Wilhelm Olejnik


People also ask

Can we give FormControlName to div?

<div> is a readonly element, usually there's no point in binding form controls to it. You can also create your own custom components (or directives) that can be bound to form controls—if they implement ControlValueAccessor interface.

What is the function of form control?

Form controls enable accessibility by taking a uniform approach to such features as captions, help text, tabbing and keyboard shortcuts. Internationalization issues are addressed by following the same design principles as in XHTML. All form controls are suitable for styling using Aural CSS (ACSS) style properties.

What is ControlValueAccessor in Angular?

Defines an interface that acts as a bridge between the Angular forms API and a native element in the DOM.


2 Answers

As we can't get NgControl instance directly from DI system since we'll get a circular dependency error. The following diagram shows why it happens if we inject NgControl in our custom value accessor:

enter image description here

Now it should be clear that we have NgControl -> FormControlName -> ValueAccessor -> CustomValueAccessor -> NgControl circular dependency

To work around it you can leverageInjector to achieve that:

component.ts

import { NgControl } from '@angular/forms';
export class PasswordComponent implements ControlValueAccessor {
  ...
  ngControl: NgControl;

  constructor(private inj: Injector) {
    ...
  }

  ngOnInit() {
    this.ngControl = this.inj.get(NgControl)
  }

template.html

{{ ngControl.control.valid }}

Plunker Example

like image 96
yurzui Avatar answered Oct 09 '22 17:10

yurzui


One more way to solve this problem is to Remove the NG_VALUE_ACCESSOR from the provider and just inject NgControl. Using the NgControl instance the component will be registered as ValueAccessor.

constructor(
..., 
@Optional() @Self() public ngControl: NgControl,
...,
) {
// Setting the value accessor directly (instead of using
// the providers) to avoid running into a circular import.
if (this.ngControl != null) { this.ngControl.valueAccessor = this; }
}
like image 29
Manoj Avatar answered Oct 09 '22 19:10

Manoj