-
Notifications
You must be signed in to change notification settings - Fork 25k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unified Control State Change Events (FormControl should also support pristine/touched/untouched/valid events) #10887
Comments
agree. say, if an input field gets focus and then lose focus without enter anything, the valueChanges/statusChanges won't fire event and therefore the validation.require warning got no way to capture. |
any news? |
related #17736 |
It feels really awkward to work with forms right now when you are trying to follow a pure stream-based approach but still have to access properties like "dirty" in a synchronous manner. Observables would be greatly appreciated! |
Any progress? If you subscribe to the statusChanges and modify some control's value the observable will emit. But if you for instance call form.markAsPristine (even if it was dirty before) nothing comes out of the observable. |
Looking into the source code, it seems the effort to implement this isn't that huge. I could imagine two new Observables, for instance Any opinions? Can the community support here? |
As a temporary workaround to be able to listen to 'dirtiness' of a form, I have implemented the following simple pseudo-code:
|
can someone post a proper solution for this and properly described? |
I have a temporary solution. Definitely sad to not see this in the framework, but I can't be waiting for it to merge anything. So I came up with a solution with unit tests on my side. Here is the code. type StatuChange = (property: string, previousValue: any, value: any) => void;
interface EmpoweredStatus {
property: string;
value: any;
previousValue: any;
}
class EmpoweredFormControl<T extends AbstractControl> {
private static readonly EMPOWER_KEY = '##empower##';
private statusChangeHandlers: StatuChange[] = [];
private empoweredStatusSubject = new Subject<EmpoweredStatus>();
static getEmpoweredControl<T extends AbstractControl>(abstractControl: T): EmpoweredFormControl<T> {
if (!abstractControl[EmpoweredFormControl.EMPOWER_KEY]) {
abstractControl[EmpoweredFormControl.EMPOWER_KEY] = new EmpoweredFormControl(abstractControl);
}
return abstractControl[EmpoweredFormControl.EMPOWER_KEY];
}
static destroyEmpoweredControl<T extends AbstractControl>(abstractControl: T): void {
if (abstractControl[EmpoweredFormControl.EMPOWER_KEY]) {
delete abstractControl[EmpoweredFormControl.EMPOWER_KEY];
}
}
private constructor(readonly abstractControl: T) {
this.empowerAbstractControl(this.abstractControl);
}
destroy() {
this.statusChangeHandlers = null;
EmpoweredFormControl.destroyEmpoweredControl(this.abstractControl);
}
registerStatusChange(fn: StatuChange): void {
this.statusChangeHandlers.push(fn);
}
get empoweredStatusChange(): Observable<EmpoweredStatus> {
return this.empoweredStatusSubject.asObservable();
}
private change(property: string, previousValue: any, currentValue: any) {
this.statusChangeHandlers.forEach(handler => {
handler(property, previousValue, currentValue);
});
}
private empowerAbstractControl(abstractControl: T) {
['touched', 'pristine'].forEach(property => {
for (let baseProperty of ['_' + property, property]) {
let propertyValue: any = abstractControl[baseProperty];
if (propertyValue !== undefined) {
Object.defineProperty(abstractControl, baseProperty, {
get: () => propertyValue,
set: (value: any) => {
let previousValue = propertyValue;
propertyValue = value;
this.change(property, previousValue, propertyValue);
}
});
break;
}
}
});
this.registerStatusChange((property: string, previousValue: any, value: any) => {
this.empoweredStatusSubject.next({
property,
value,
previousValue
});
});
}
} |
My use-case is that I created a <!-- some-form.component.html -->
<cw-form-errors [control]="form.controls.name" [force]="submitted"></cw-form-errors>
<!-- form-errors.component.html -->
<div class="invalid-feedback" *ngIf="control.invalid && (control.touched || force)">
<div [hidden]="!control.errors[key]" *ngFor="let key of keys">{{ errors[key] }}</div>
<ng-content></ng-content>
</div> After adding <cw-form-errors [control]="form.controls.name" [show]="form.controls.name.touched || submitted"></cw-form-errors> This issue has A state object containing each of the state booleans that gets emitted and can be checked: interface FormState {
dirty: boolean;
pending: boolean;
pristine: boolean;
touched: boolean;
untouched: boolean;
}
control.stateChanges: Observable<FormState>; Or, like statusChanges and valueChanges, an Observable for each state individually: control.dirtyChanges: Observable<boolean>;
control.dirtyChanges: Observable<boolean>;
control.pendingChanges: Observable<boolean>;
control.pristineChanges: Observable<boolean>;
control.touchedChanges: Observable<boolean>;
control.untouchedChanges: Observable<boolean>; |
Similar to @intellix, I have a component that show input errors, and because I can't listen the changes of touched and dirty through the control object I'm using Among hundreds of components in the project, this is the only one with I hope that this issue be fixed soon. I don't see why it's taking too long. I think the 2 best options to do are either using a state object to be passed in the |
This feature is taking quite some time to get designed. Any news? |
Wanted to drop a line and say I've bumped into this as well. Exactly the same use cases as the people before me. Using OnPush and wanting to have a structural directive that will hide/show errors but only after the control has been touched or the form has been submitted. Ended up having to add a superficial input on the directive called 'touched' so that change detection would pick up on it. An easy hack to remove in the future should this ever be addressed. |
It seems really odd that, with all the performance improvements in Angular over AngularJS, we still don't have a way to listen for this instead of having to bind to function calls that get called on each digest cycle |
Any up on this issue ? It's really weird that we didn't get any news yet about this mandatory feature ?! |
One easy workaround until this feature will be a reality is to wrap the formGroup.statusChanges.subscribe(status => {
console.log(formGroup.pristine); // Not changed
setTimeout(() => {
console.log(formGroup.pristine); // Changed
}, 0);
}); In order to notify the pristine you can create a cold observable and pass changes within the setTimeout. I guess another option can be done using I don't like this solution, personally. In an average running this works fine but in the end the functionality is relying on how the VM manages the process, so you could end up having random unexpected issues. Use it with care. EDIT: as @lucasbasquerotto pointed out this workaround does not work completely. For |
@emoriarty I don't think what you posted is a solution for this issue. The main problem I see is not about updating the The way it is currently, I can't listen to changes in the Edit: I'm not completely sure about |
Have to bump this too since no reply from the team was ever posted on this and it is a rather requested feature and a rather simple one as well. I'm with the guys above here, with my entire UI library using OnPush and that scoundrel FieldError component spoiling the party. |
Well, temporar workarround exists in creation of derived classes for *Control, FormGroup and FormBuilder... however, real solution should be very easy to implement, wonder why it isn't in reactive forms till start. |
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887
❤️💯💪🏾
…On Wed, Apr 3, 2024, 12:12 PM Jessica Janiuk ***@***.***> wrote:
Closed #10887 <#10887> as
completed via 1c736dc
<1c736dc>
.
—
Reply to this email directly, view it on GitHub
<#10887 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AANPRFLJ7SEDOLXKRMOTSYLY3Q2AXAVCNFSM4CM2KH2KU5DIOJSWCZC7NNSXTWQAEJEXG43VMVCXMZLOORHG65DJMZUWGYLUNFXW4OZRGIZTINJVGQYTCOBU>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
🫡 |
Is there a defined order in which the events are emitted if more than one thing changes? If a new value is entered, status and value events are emitted. What could I do to filter the valid values of the event stream? Is the value on the control uptodate when the status event fires so I can get it from the source? I expect a signal based form integration in some future, which should fit better... Don't feel pushed. 🍻😎 I'm curious to experiment with this new event API. |
@flensrocker We wanted to ship it early to get as mush feedback as possible before the final release. We appreciate any constructive feedback. Expect a release in the 18.0.0-next.3 version probably later today. Btw your suggestion led to #55198 👍 |
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887 PR Close angular#54579
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887 PR Close angular#54579
This commit introduces a new method to subscribe to on every `AbstractControl` subclass. It allows to track value, pristine, touched and status changes on a given control. Fixes angular#10887 PR Close angular#54579
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Original report is below -- we have merged #42862 into this bug while organizing our issue tracker.
Which @angular/* package(s) are relevant/releated to the feature request?
forms
Description
The ability for
AbstractControl
-based classes (such asFormControl
) to produce more events is a highly requested feature (lots of feedback in #10887, also #41088 and others; #24444 is also a use case). Adding more observables similar to the currentvalueChanges
andstatusChanges
is the most straightforward way to extendAbstractControl
, however, as the number of such observables increases (for examplepristine
,dirty
, etc) it might become less ergonomic and more expensive to maintain. Additionally, if we wish to support the same state changes inControlValueAccessor
, we would be duplicating a lot of individual cases.Proposed solution
We could introduce a new observable (and still support
valueChanges
andstatusChanges
for backwards compatibility) containing all events produced in various situations, so it's possible to subscribe, stream, and filter as needed. Each event in this stream might contain additional meta information such as a reference to an instance that produced the event, event type, etc. We would need a unified payload design that works with all the event types, and satisfies the use cases in the above linked issues.However before making a final decision, we'd need to perform more research and design work, to make sure that this approach is viable.
Also note that adding this stream could simplify
ControlValueAccessor
, as per #24444 and #27315.Alternatives considered
Adding individual observables on a case-by-case basis is likely infeasible, both from an API consistency perspective and an API bloat perspective.
I'm submitting a ... (check one with "x")
Current behavior
FormControl doesn't have an observable to subscribe to for when one of the following has changed:
pristine/touched/untouched/valid (well, okay, it does for valid, but i want to include the others)
statusChanges just tells me VALID or INVALID and only fires when valid changes.
i need to know when touched changed
Expected/desired behavior
either an observable for every one or one that combines them all...
fc.touchedChanges.subscribe(x => ....stuff);
What is the motivation / use case for changing the behavior?
The reason i want this is that i am making little form components and sending FormControl as input to one of them. I don't want a lot of logic in the component's html.
i would like to avoid <span *ngIf="myControl.valid && myControl.pristine || !myCointrol.blah ...
instead, i want: <span *ngIf="showWarning">
and showWarning could be set from a subscription in the component looking at pristine/touched etc
The text was updated successfully, but these errors were encountered: