如何在组件输入是嵌套对象数组时检测更改?

时间:2018-04-09 09:49:35

标签: angular input angular-components angular-forms angular-changedetection

我正在开发一个Angular应用程序,我会使用ngOnChanges在组件的输入发生变化时收到通知。在我的情况下,它没有被触发,因为我有一个嵌套对象数组作为组件的输入。

我有一个表单,其中我使用了两个子组件,这两个子组件使用同一个父FormControl的两个FormGroup。我将第一个formControl的value传递给第二个子组件,但由于在数组修改时没有发生更改检测,因此我无法在第二个子组件的输入中获取新值。

这是我的代码:

@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
        });
    }
}

这是模板文件:

<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>

第一个子组件的表单控件中所做的任何更改都不会触发第二个子组件的ngOnChanges,因为它会绑定数组引用而不是数组内容。因此,对数组的任何对象所做的任何更改或对数组本身所做的任何更改都不会更改数组引用,然后将不会调用ngOnChanges()

可能的解决方案可能是使用ngDoCheck。但它可能会有一个可怕的成本,因为它的频率很高。它变得很重,即使我应该使用IterableDiffersKeyValueDiffers

另一种解决方案可能是订阅第一个formControl的valueChanges,但我无法弄清楚为什么它似乎不能正常工作,有时它会错过一些变化......

在我的案例中,什么是最好的解决方案?

3 个答案:

答案 0 :(得分:2)

我解决了在第一个子组件上添加Output的问题。当数组更改时,它使用EventEmitter向父组件发出事件。 然后父组件更改数组引用,以便第二个子组件检测到更改并触发其ngOnChanges

以下是代码:

@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);
    };
}

模板:

<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>

另一种解决方案可能是使用localStorage而不是OutputEventEmitter:第一个子组件将数据(如果已更改)设置为本地存储;第二个子组件从中获取数据。

答案 1 :(得分:0)

您可以从sub-component收听表单更改,一旦有更改,请手动运行更改检测。

这实际上非常有效并且表现良好。

从&#39; @ angular / core&#39;

导入{ChangeDetectorRef}
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()
          })

    }
}

注意debounceTime只是另一个性能提升很好,使用它会让我们减少更改检测调用的次数

答案 2 :(得分:0)

您可以将整个form:FormGroup传递给子组件,然后无需考虑如何检测每个子组件中的更改。

StackBlitz EXAMPLE

  

我正在开发一个Angular应用程序,我会使用ngOnChanges来获取   当组件的输入发生变化时通知。在我的情况下,它不是   因为我有一个嵌套对象数组作为输入   成分

此外,如果您确实需要检测数组值更改,可以使用ngDoCheck

NgDoCheck()检测并执行Angular无法或无法自行检测的更改