2

I'm developing an Angular app and I would use ngOnChanges to get notified when the Input of a component changes. In my case it is not fired because I have an array of nested object as Input for the component.

I have a component with a form in which I use two sub-components that use two FormControl of the same parent FormGroup. I pass the value of the first formControl to the second sub-component, but since no change detection occurs on array modification I can't get the new values in the Input of the second sub-component.

Here is my code:

@Component({
    encapsulation: ViewEncapsulation.None,
    moduleId: module.id,
    selector: 'my-component',
    templateUrl: 'my-component.html',
    styleUrls: ['my-component.scss']
})
export class MyComponent {
    myForm: FormGroup;

    constructor(private fb: FormBuilder) {
        this.myForm = fb.group({
            documents: null,
            summary: null
        });
    }
}

Here is the template file:

<div class="content">
    <form class="ui form" [formGroup]="myForm">
        <my-component-child-one [form]="myForm.get('documents')"></my-component-one>

        <my-component-child-two [form]="myForm.get('summary')" [content]="myForm.get('documents').value"></my-component-two>
    </form>
</div>

Any change made in the form control of the first sub-component does not trigger ngOnChanges of the second sub-component, because it binds the array reference instead of array content. So any change made to any object of the array or any change made to the array itself doesn't change the array reference and then ngOnChanges() will not be called.

A possible solution may be using ngDoCheck. but it could have a frightful cost because it is called with enormous frequency. It becomes heavy even because I should use IterableDiffers and KeyValueDiffers.

Another solution could be subscribing to valueChanges of the first formControl, but I cannot figure out why it seems not to work propertly, sometimes it misses some changes...

What could be the best solution in my case?

2
  • what do you mean it misses your changes ? it's impossible . is your component OnPush ? Commented Apr 9, 2018 at 9:58
  • What do you mean for OnPush? Commented Apr 9, 2018 at 12:04

3 Answers 3

2

I solved adding an Output on the first sub-component. When the array changes it uses an EventEmitter to emit an event to the parent component. Then the parent component changes the array reference so the second sub-component detects changes and its ngOnChanges is triggered.

Here is the code:

@Component({
    encapsulation: ViewEncapsulation.None,
    moduleId: module.id,
    selector: 'my-component',
    templateUrl: 'my-component.html',
    styleUrls: ['my-component.scss']
})
export class MyComponent {
    myForm: FormGroup;
    documents: Array<Document>;

    constructor(private fb: FormBuilder) {
        this.myForm = fb.group({
            documents: null,
            summary: null
        });
    }

    documentsChanged = () => {
        this.documents = [].concat(this.myForm.get('documents').value);
    };
}

Template:

<div class="content">
    <form class="ui form" [formGroup]="myForm">
        <my-component-child-one [form]="myForm.get('documents')" (docs-changed)="documentsChanged()"></my-component-one>

        <my-component-child-two [form]="myForm.get('summary')" [content]="documents"></my-component-two>
    </form>
</div>

Another solution could be using localStorage instead of Output and EventEmitter: the first sub-component sets data, if it changed, to the local storage; the second sub-component gets data from it.

Sign up to request clarification or add additional context in comments.

Comments

0

You can pass entire form:FormGroup to child components and then no need to think about how to detect changes in each child components.

StackBlitz EXAMPLE

I'm developing an Angular app and I would use ngOnChanges to get notified when the Input of a component changes. In my case it is not fired because I have an array of nested object as Input for the component.

Also, if you really need to detect array value changes, you can use ngDoCheck.

NgDoCheck() detect and act upon changes that Angular can't or won't detect on its own

2 Comments

@smartmouse, yes. But it's in the second case of my answer
How does the first case work? What are the advantages of passing entire form?
0

You can listen to the form changes from your sub-component and once there is a change, run the change detection manually.

This is actually very efficient and performs well.

import {ChangeDetectorRef} from '@angular/core'

export class MySubComponent {
    @Input() form : FormGroup;
    constructor(private _cd:ChangeDetectorRef){}
    ngOnInit() {
        this.form.valueChanges.debounceTime(100).subscribe(()=>{
      //this._cd.markForCheck() only of your component is OnPush
             this._cd.detectChanges()
          })

    }
}

Note the debounceTime is just another performance boost which is nice, using it will let us reduce the number of change detection calls

1 Comment

As I said in my question this was one of the solution I tried, but I cannot figure why sometimes it doesn't detect changes. What is _cd?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.