I’m struggling with some strange behavior while using my custom input component.
First of all, I built a simple abstract class that has the main “features” and methods of the component, then, the input-component which has very few code:
// Abstract class
export abstract class BaseFormInput<T> implements ControlValueAccessor, Validator, AfterViewInit, OnDestroy {
@Input() label: string
@Output() onChange: EventEmitter<T> = new EventEmitter<T>()
private changeInternal: (obj: T) => void
private changeSub: Subscription
private disabled$ = new BehaviorSubject(false)
private required$ = new BehaviorSubject(false)
public input = new FormControl(null)
ngOnDestroy() {
this.changeSub.unsubscribe()
}
ngAfterViewInit() {
this.changeSub = this.input.valueChanges.subscribe(v => {
if (!this.disabled$.getValue()) {
this.onChange.emit(v)
this.changeInternal(v)
}
})
}
writeValue = (obj: T) => this.input.setValue(obj)
registerOnChange = (fn: (obj: T) => void) => this.changeInternal = fn
registerOnTouched = (_fn: (obj: any) => void) => {}
setDisabledState = (isDisabled: boolean) => this.disabled$.next(isDisabled)
validate(control: AbstractControl): ValidationErrors {
this.required$.next(control.hasValidator(Validators.required))
// THIS LINE HAS WEIRD BEHAVIOR
console.log(control, control.errors)
return null
}
public get isDisabled$(){
return this.disabled$.asObservable()
}
public get isRequired$(){
return this.required$.asObservable()
}
}
The input component is simply designed like this:
@Component({
selector: "ec-input-text",
template: `<div class="form-control">
<label *ngIf="label">
{{ label }}
<span *ngIf="isRequired$ | async">*</span>
</label>
<input *ngIf="type !== 'textarea'" [type]="type" [formControl]="input" [attr.disabled]="isDisabled$ | async" />
<textarea *ngIf="type === 'textarea'" [formControl]="input" [attr.disabled]="isDisabled$ | async"></textarea>
<ng-template></ng-template>
</div>`,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => InputTextComponent), multi: true },
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => InputTextComponent), multi: true }
]
})
export class InputTextComponent extends BaseFormInput<string> {
@Input() type: "text" | "password" | "email" | "textarea" = "text"
@Input() maxLength: number
}
Finally, I created a register-component, which uses the input.
HTML:
<form [formGroup]="form">
<ec-input-text label="First name" formControlName="firstName" />
<ec-input-text label="Last name" formControlName="lastName" />
<ec-input-text label="E-mail" formControlName="email" type="email" />
<ec-input-text label="Password" formControlName="password" type="password" />
</form>
The TS of the register-component has a public property like this:
public form = new FormGroup({
firstName: new FormControl(null, [Validators.required, Validators.maxLength(50)]),
lastName: new FormControl(null, [Validators.required, Validators.maxLength(50)]),
email: new FormControl(null, [Validators.required, Validators.maxLength(100)]),
password: new FormControl(null, Validators.required)
})
Now, the issue is the following: in the validate method of the abstract class (where I put a comment), I tried to log the control errors, and I get a strange behavior: when logging the formControl, I can see in the console that the property errors is null, but if I log control.errors it logs:
{ required: true }
Even though the control is valid and I typed the value (in fact, control.value has a value and results valid).
So if i do:
console.log(control)
And I expand it, errors is null (expected behavior, correct!)
But if I do:
console.log(control.errors)
It is valorized (not correct, the control is valid!)
How can I figure this out? Thanks in advance!