Change detection does not trigger when the formgroup values change

别说谁变了你拦得住时间么 提交于 2019-12-23 08:56:06

问题


I have created a simple example to demonstrate a weird issue I'm facing.

Stackblitz - https://stackblitz.com/edit/angular-change-detection-form-group

I have three components and here are they:

1 - app component

import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';

@Component({
  selector: 'my-app',
  template: `<hello [form]="form"></hello>
  <hr />
  <button (click)="changeFormValue()">Change Form Value</button>`,
  styleUrls: ['./app.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush

})
export class AppComponent implements OnInit {
  name = 'Angular';

  form: FormGroup;

  ngOnInit() {
    this.form = new FormGroup({
      name: new FormControl('ABC'),
      age: new FormControl('24')
    });
  }

  changeFormValue() {
    this.form.setValue({
      name: 'XYZ',
      age: 35
    })
  }
}

2 - hello component

import { Component, Input, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
  selector: 'hello',
  template: `<form [formGroup]="form">
  <app-input [form]="form"></app-input>
  </form>`,
  styles: [``],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class HelloComponent implements OnChanges {
  @Input() form: FormGroup;

  ngOnChanges(changes) {
    console.log(changes)
  }
}

3 - input component

import { Component, Input, OnInit, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
  selector: 'app-input',
  template: `Name : <input type="text" [formControl]="nameFormcontrol" /> {{nameFormcontrol.value}} <br /><br />
  Age : <input type="text" [formControl]="ageFormcontrol" /> {{ageFormcontrol.value}}`,
  styles: [``],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class InputComponent implements OnInit, OnChanges {
  @Input() form: FormGroup;
  nameFormcontrol;
  ageFormcontrol;

  ngOnInit() {
    this.nameFormcontrol = this.form.get('name');
    this.ageFormcontrol = this.form.get('age');
  }

  ngOnChanges(changes) {
    console.log(changes)
  }
}

In both hello component and input component, I have set the changedetection strategy to onpush. As you see above I am creating a form group instance in the app component and passing that to the child components. Now when I click on the button on app component to change the form value, it changes the value in the input fields but not the plain texts. It only works if I delete the on push change detection from both the child components. Even the ngOnChanges does not get called even if the formgroup values are changing.

Is n't it weird. How the change detection works for inputs and not for the plain texts here?

Could someone explain me this please? And what is the workaround of it without removing the onpush change detection.


回答1:


I found a workaround to this problem although I am not sure if this is the ideal solution.

We can listen to the form group value changes and then trigger change detection in the input component

this.form.valueChanges.subscribe( () => {
  this.cdr.detectChanges()
});

This way it updates the label values as well along with the inputs.

Here is the solution:

https://stackblitz.com/edit/angular-change-detection-form-group-value-change-issue-resolved

I'm not sure if it's a bug from Angular but happy that I figured out some workaround :)




回答2:


During the automatic change detection (cd) run Angular would notice that the values of your FormGroup have been updated and updates the UI respectively.

By setting the change detection strategy to OnPush you deactivates the automatic change detection run by Angular. In this case only the internal values are updated, but Angular would not check the values of the UI.

Maybe this is a too superficial explanation of Angular's ChangeDetection, so I recommend this blog for a deeper look into this topic.

ngOnChanges does not trigger, because the object reference (memory address) of your FormGroup has not been changed. ngOnChanges is only triggered if you pass primitives to the @Input of your components. And this would also trigger a new change detection run by Angular.

To update the UI you can trigger the change detection manually by injecting ChangeDetectorRef in your parent component and calling detectChanges().

This could look like this:

constructor(private cd: ChangeDetectorRef) {}
...
changeFormValue() {
    this.form.setValue({
        name: 'XYZ',
        age: 35
    });
    // This will trigger the change detection and your input field are updated
    this.cd.detectChanges(); 
}

I hope this is understandable ;-)




回答3:


Angular only detects changes if the memory address of the variable changes. Setting the value does not change memory address, thus does not hit ngOnChanges.

Same goes with arrays. A simple push does not hit ngOnChanges, have to change memory address by = to new array.

Try this:

import { Component, Input, OnInit, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
  selector: 'app-input',
  template: `
  <div formGroupName="form">
      Name : <input type="text" formControlName="name" /> {{form.value.name}} <br /><br />
      Age : <input type="text" formControlName="age" /> {{form.value.age}}
  </div>`,
  styles: [``],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class InputComponent implements OnInit, OnChanges {
  @Input() form: FormGroup;

  ngOnInit() {
  }

  ngOnChanges(changes) {
    console.log(changes)
  }
}


来源:https://stackoverflow.com/questions/53640559/change-detection-does-not-trigger-when-the-formgroup-values-change

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!