Showing Validation messages with Reactive Forms and ChangeDetectionStrategy.OnPush
P

3

10

I'm trying to migrate an app to using ChangeDetectionStrategy.OnPush. However, I'm running into a bit of a blocker trying to work with some reactive forms.

I've got a reusable control that shows validation messages based on some of the state flags (e.g. *ngIf="control.touched && control.invalid"). The problem I'm running into is that there's no way to detect when the touched flag changes, and therefore no way to trigger change detection using ChangeDetectorRef.

It could be done with a reference to the input element itself by listening for a click event, but that doesn't account for when markAsTouched() is used, and passing a reference to the element around isn't always possible, and it's always inelegant.

Is there a way to use OnPush and still respond to the form control state (e.g. touched), or is it just generally painful?

Pivoting answered 4/5, 2019 at 0:19 Comment(4)
lots of alternatives: blog.angular-university.io/onpush-change-detection-how-it-worksMukden
@pixelbits - that's just basically a walkthrough of how to use OnPush. Editing my question to clarify my main issue.Pivoting
did you find any solution?Fortnightly
@Fortnightly not really. Ultimately, there isn't really a great way to use OnPush with Reactive form components unless Angular gets updated to add observables for those state changes. I ended up leaving the components with forms using the default strategy, and then using OnPush for everything else.Pivoting
M
1

You can listen for status changes and then call markForCheck() to update the view:

constructor(changeDetectorRef: ChangeDetectorRef) {
  this.formGroup.statusChanges.subscribe(status => changeDetectorRef.markForCheck());
}

UPDATE: Since I can't comment, I will update this answer.

You are right about statusChanges. and of course you need to subscribe to statusChanges, it was a mistake.

I think we missed a point here. In CheckOnce mode (OnPush), Angular will check for changes after DOM events (including blur), so the view should be updated and the message you metioned (*ngIf="control.touched && control.invalid") should work.

But if you call markAsTouched() in the code will not check for changes until you call markForCheck().

Here is a working example: https://stackblitz.com/edit/angular-6-reactive-form-validation-touched-sjhijt

Marchpane answered 7/5, 2020 at 14:9 Comment(1)
statusChanges only deals with the validation state of the control (valid/invalid/dirty/pristine), and doesn't emit due to touched changing - angular.io/api/forms/AbstractControl. Also, FWIW, your syntax is incorrect ;) statusChanges is an observable, so you need to call .subscribe on it rather than just passing it a callback.Pivoting
U
0

I do not know if you are looking for the new way to create a ReactiveForm using the new FormGroup directly. Sorry if I did not understand your question. e.g.

//before
this.myForm=this.formBuilder({
   control1:''
   })
//after
this.myForm=new FormGroup({
   control1:new FormControl(''),
},{ updateOn: 'blur' })
Underexposure answered 4/5, 2019 at 9:8 Comment(1)
Not exactly - the problem I'm particularly running into is triggering an update based on the "touched" property changing. For example, when a parent component calls "markAsTouched" on a control.Pivoting
G
0

You can overwrite the original markAsTouched method of your control with a method that also calls markForCheck:

const originalMarkAsTouched = this.control.markAsTouched;
this.control.markAsTouched = () => {
  originalMarkAsTouched.call(this.control);
  this.changeDetectorRef.markForCheck();
};
Glutinous answered 3/5 at 21:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.