8

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?

Daniel Schaffer
  • 56,753
  • 31
  • 116
  • 165
  • lots of alternatives: https://blog.angular-university.io/onpush-change-detection-how-it-works/ – Michael Kang May 04 '19 at 01:37
  • @pixelbits - that's just basically a walkthrough of how to use OnPush. Editing my question to clarify my main issue. – Daniel Schaffer May 06 '19 at 15:13
  • did you find any solution? – miladfm Feb 20 '20 at 15:16
  • 2
    @miladfm 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. – Daniel Schaffer Feb 20 '20 at 19:11

2 Answers2

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

WaseemDev
  • 11
  • 2
  • 3
  • 1
    `statusChanges` only deals with the validation state of the control (valid/invalid/dirty/pristine), and doesn't emit due to `touched` changing - https://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. – Daniel Schaffer May 07 '20 at 14:34
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' })
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • 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. – Daniel Schaffer May 06 '19 at 15:06