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;
};
}