-
Notifications
You must be signed in to change notification settings - Fork 26.7k
Description
Which @angular/* package(s) are relevant/related to the feature request?
forms
Description
It is still impossible to implement working custom CVA based components in 50% of scenarios.
Main cause of this is non-existing way how to get ALL information about AbstractControl.
Why?
- there is not any 100% working way how to get information about important control state (valid/invalid/pristine/touched).
- it is important to know control states like valid/invalid/pristine/touched to correctly implement smarter components than hello world
Why current implementation does not work?
-
CVA interface does not provide ANY way how to get information about status changes (it can only react to ValueChanges - writevalue and disabled changes - setDisabled), there is not anything like setValid(), setTouched() pristine etc.
-
You can get ngControl from injector (because CVA is solving only 20% of domain...) .Control has now "unified events api" https://angular.dev/api/forms/AbstractControl#events unfortunately, it is preserved when {emitEvent: false} during the method calls
-
It is EVEN BIGGER Problem as far as disabled control has BOTH valid and invalid SET TO FALSE (lol), so you just DO NOT HAVE ANY way how to get any information about the state of validity, when your control is enabled with {emitEvent:false} (check source code below)
-
it is so sad that the only way how to get 100% information about control state is to LISTEN for DOM mutation on Host element (to read ng-invalid, ng-valid, ng-pending, ng-prisitine, etc. classes)
-
the same problem when validity of the control changes when the new value is set - you have NO WAY how to get the new valid / invalid status when user patch/set value with emitEvent: false. You cannot even handle this inside of writeValue() because validators are executed after that.... So not any workaround..
FORCING user not to use {emitEvent:false } which is his way to prevent emiting valueChanges "ON THE OUTSIDE SIDE" - aka. consumer of the component (in bigger form) is definitely not a solution to this problem, where Component creator has not any legit way how to get state of the control.
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormControl, PristineChangeEvent, ReactiveFormsModule, StatusChangeEvent, TouchedChangeEvent} from '@angular/forms';
import { bootstrapApplication } from '@angular/platform-browser';
@Component({
selector: 'app-root',
standalone: true,
imports: [ReactiveFormsModule, CommonModule],
template: `
<input type="number" [formControl]="number"/>
Valid: {{number.valid}}
Invalid: {{number.invalid}}
Errors: {{number.errors | json}}
<button (click)="number.enable({emitEvent: false})">ENABLE</button>
<hr/>
Problems:
<ul>
<li>Disabled input has both VALID and INVALID set TRUE - eventhogh there is no validator and it NO ERRORS</li>
<li>When you enable control - it is now valid, but NO events is fired from Control.Events</li>
</ul>
<hr/>
Events:
<ul>
@for(event of events; track $index) {
<li>{{event | json}}</li>
}
</ul>
`,
})
export class App {
events: any[] =[];
number = new FormControl();
ngOnInit( ){
this.number.disable();
this.number.events.subscribe(e=> {
if (e instanceof StatusChangeEvent) {
this.events.push("Status - " + e.status);
} else if (e instanceof TouchedChangeEvent) {
this.events.push("Touched - "+ e.touched);
} else if (e instanceof PristineChangeEvent) {
this.events.push("Pristine - "+ e.pristine);
}
}
);
}
}
bootstrapApplication(App);
Proposed solution
- more methods in CVA interface
- event emitter on AbstractControl which emits everytime, regardless the emitEvent settings
Alternatives considered
- our only WAY how to solve this is to write our own AbstractControl's implementations (inherit the base ones from angular) and to add additional event emiiters...
- in this case where is the main problem the valid/invalid state of control, you can "rebind" your internal valid/invalid/pristine/touched state when setDisabled() of CVA is called.
- in case of changed valid/invalid state when the new value is set to control with emitEvent:false, there is not any way except to "queue" sync check after "writeValue"
Just sad state.