I have a custom input component that is updating validation and states with the exception of touched/untouched. Everything else state-wise (pristine/dirty) works as expected.
Here's a plunker: https://plnkr.co/edit/O9KWzwhjvySnXd7vyo71
import { Component, OnInit, Input, ElementRef, forwardRef, Renderer } from '@angular/core';
import { REACTIVE_FORM_DIRECTIVES, Validator, Validators, NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms';
export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = /*@ts2dart_const*/ {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
};
const noop = () => {};
@Component({
selector: 'my-custom-input',
providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR],
template: `
<div class="form-group">
<label>CUSTOM INPUT</label>
<input type="text" class="form-control" [(ngModel)]="value" required>
<p *ngIf="control.errors.required && control.touched">Field is required</p>
<strong>Has input been touched: {{control.touched ? 'Yes' : 'No'}}</strong><br>
<strong>Is input untouched: {{control.untouched ? 'Yes' : 'No'}}</strong><br>
<strong>Is input dirty: {{control.dirty ? 'Yes' : 'No'}}</strong> <br>
<strong>Is input pristine: {{control.pristine ? 'Yes' : 'No'}}</strong>
</div>
<div>
In Custom Component: {{value}}
</div>
`
})
export class CustomInputComponent implements ControlValueAccessor {
@Input() control;
// The internal data model
private _value: any = '';
//Placeholders for the callbacks
private _onTouchedCallback: (_:any) => void = noop;
private _onChangeCallback: (_:any) => void = noop;
//get accessor
get value(): any { return this._value; };
//set accessor including call the onchange callback
set value(v: any) {
if (v !== this._value) {
this._value = v;
this._onChangeCallback(v);
}
}
//Set touched on blur
onTouched(){
this._onTouchedCallback(null);
}
//From ControlValueAccessor interface
writeValue(value: any) {
this._value = value;
}
//From ControlValueAccessor interface
registerOnChange(fn: any) {
this._onChangeCallback = fn;
}
//From ControlValueAccessor interface
registerOnTouched(fn: any) {
this._onTouchedCallback = fn;
}
}
Thanks for any help!
I was making two mistakes, like a knobhead. So the template needs to be:
So the two things where the
(blur)="onTouched($event)"
on the input, and the<p *ngIf="control?.errors?.required && control?.touched">
Just stepped on @sharpmachine answer and it helped to solve my problem. I just would like to improve it:
Instead of having to bind the
blur
event toonTouched()
at the template level (which can be error-prone) it is possible to expose theControlValueAccessor
as aDirective
and bind the event there.This way you should be able to use the component without having to bind the
blur
event each time you use it.Hope it helps!