Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular - controlValueAccessor method writeValue() not being called

Tags:

angular

Angular Version: 6.1

I'm trying to implement a custom control using what Kara Erickson is calling a Composite ControlValueAccessor as shown in the presentation, Angular Forms – Kara Erickson – AngularConnect 2017. The control's value should be set to one of the two child input values.

The issue is writeValue() seems to only be called initially and not on further value changes.

Here's a stackblitz demo.

form.html

<form #f="ngForm" (ngSubmit)="f.form.valid && Submit(f)">
    <confirm-password name='password' ngModel #pwd='ngModel'></confirm-password>
    <div>{{pwd.status}}</div>
</form>

confirm-password.html

<div [formGroup]='pass'>
  <label>Password:
    <input type="password" formControlName='pwd1' (blur)='onTouched()'>
  </label>
  <div *ngIf="controlDir.touched && controlDir.control.errors?.required" 
         class="error">Password is required.</div>

  <label>Please re-enter password:
    <input type="password" formControlName='pwd2' (blur)='onTouched()'>
  </label>
  <div class='error' *ngIf='controlDir.touched && controlDir.control.errors?.notEqual && 
                            !controlDir.errors?.required'>Passwords do not match.</div>
</div>

confirm-password.ts

import { Component, Self, OnInit, OnDestroy } from '@angular/core';
import { ControlValueAccessor, AbstractControl, NgControl,
         ValidatorFn, Validators, FormGroup, FormControl } from '@angular/forms';
import { Subscription } from 'rxjs';

@Component({
  selector: 'confirm-password',
  templateUrl: './confirm-password.component.html',
  styleUrls: ['./confirm-password.component.css']
})
export class ConfirmPasswordComponent implements ControlValueAccessor, OnInit, OnDestroy
{
  password: string; // the value of the control should be set to this value
  pass = new FormGroup({
    pwd1: new FormControl(),
    pwd2: new FormControl()
  });
  private onChange: (value: string) => void;
  private onTouched: (value: string) => void;
  private valueChanges: Subscription;

  constructor(@Self() public controlDir: NgControl)
  {
    controlDir.valueAccessor = this;
  }

  ngOnInit()
  {
    const control = this.controlDir.control;

    let myValidators = [
            Validators.required, 
            equalValidator(this.pass.controls.pwd1,
                           this.pass.controls.pwd2)
        ]
    // ovoid overwriting any existing validators
    let validators = control.validator
      ? [control.validator, ...myValidators]
      : [...myValidators];
    control.setValidators(validators);
    control.updateValueAndValidity();
  }

  writeValue(val: any)
  {
    /* whether everything inside of this method is commented out or not 
       doesn't seem to affect anything */
    console.log(val);
    //val && this.pass.setValue(val, {emitEvent: false});
    this.password = val;
  }

  registerOnChange(fn: (val: any) => void)
  {
    this.valueChanges = this.pass.valueChanges.subscribe(fn);
  }

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

  setDisabledState(disabled: boolean)
  {
    disabled ? this.pass.disable() : this.pass.enable();
  }

  ngOnDestroy()
  {
    this.valueChanges.unsubscribe();
  }

}

export function equalValidator(el1, el2): ValidatorFn
{
  return (ctrl: AbstractControl): {[key: string]: any} | null => {
    const notEqual = el1.value !== el2.value;

    return notEqual ?  {"notEqual": true} : null;
  };
}
like image 502
slanden Avatar asked Jul 27 '18 21:07

slanden


People also ask

What is writeValue in Angular?

The writeValue method is used by formControl to set the value to the native form control. The registerOnChange method is used by formControl... Read more > Implementing Control Value Accessor in Angular | by Majd Asab.

What is ControlValueAccessor in Angular?

A ControlValueAccessor acts as a bridge between the Angular forms API and a native element in the DOM. Any component or directive can be turned into ControlValueAccessor by implementing the ControlValueAccessor interface and registering itself as an NG_VALUE_ACCESSOR provider.

What is Ng_value_accessor?

NG_VALUE_ACCESSORlinkUsed to provide a ControlValueAccessor for form controls.

What is registerOnChange Angular?

registerOnChange()linkRegisters a callback function that is called when the control's value changes in the UI. registerOnChange(fn: any): void.


1 Answers

The purpose of writeValue is to inform your component about changes on the outside / the form control on parent component. What you'll usually want to do with it is to bind the change from outside to a local variable.

In order to inform the outside world about changes inside of your component, you need to call the onChange method. You can call it, like you would usually call an event emitter.

I've put together a couple of examples here: https://www.tsmean.com/articles/angular/angular-control-value-accessor-example/

like image 169
bersling Avatar answered Sep 25 '22 07:09

bersling