角度输入设置器仅触发一次

时间:2018-07-10 12:16:41

标签: angular typescript setter angular-decorator

我想开头一个问题,说我已经阅读并理解了其他可能重复的问题(例如this one),并且无法进行此项工作,使我认为该问题本质上有点不同否则我会误会了。

上下文:我有一个父组件,其中有两个子组件(侧面菜单和主视图)。侧面菜单中有一些过滤器,形式是复选框,它们通过[(ngModel)]绑定到对象,并使用模板中的(ngModelChange)="changeFilter()"触发事件。

在父组件中,我捕获了事件:(filter)="updateFilters($event)"。此函数更改了一个公共过滤器变量,然后我将其传递给其他子组件,例如:[filterObject]="filters"

在主视图组件中,我将filterObject变量提供给管道以过滤对象数组。

问题:在我第一次更换过滤器后,管道仅被调用一次。像{{filterObject | json}}这样显示时,主视图内的filterObject变量显示正确更新,但从未调用setter。在setter和getter中放置的控制台日志显示,只有getter可以访问(2-4)

我尝试过的解决方案:

  • 向管道添加一个附加参数以触发它。这种方法的问题在于,需要从filterObject的设置器中设置一个新日期,而不会被调用-如上所述。

  • 使管道不纯净(可行,但不是一个很好的解决方案,因为它使每次单击可处理2-4个非常大的复杂对象数组)

  • 不是通过赋值运算符,而是通过Object.assign来使父级更新过滤器变量,以更改引用并可能触发设置器。

我正在考虑的解决方案:如果我无法弄清楚问题出在哪里,我正在考虑提供一种通过观察者在两个组件之间进行通信的服务。否则,我可能不得不解决嵌入了一些自定义更改检测的不纯管道(我正在考虑将过滤器对象转换为JSON字符串并检查长度)。

side-menu-component.html

<input type="checkbox" id="filter-type-2" name="filter-type-2" [(ngModel)]="filterObject.type.me" (ngModelChange)="changeFilter()">

side-menu-component.ts

 @Output() filter = new EventEmitter();
 public filterObject = {
    type: {
        mo: true,
        me: true,
        ar: true,
        cto: true,
        gl: true,
        dl: true
    },
    status: {
        unreviewed: true,
        approved: true,
        other: true
    }
};

// ...

changeFilter() {
    this.filter.emit(this.filterObject);
}

parent-component.html

<aside>
     <app-article-view-aside [article]="article" (close)="closeModal($event)" (filter)="updateFilters($event)"></app-article-view-aside>
</aside>
<div class="articleContainerMainView">
     <app-article-view-main [article]="article" [filterObject]="filters"></app-article-view-main>
</div>

parent-component.ts

updateFilters(newFilter) {
    this.filters = newFilter;
}

main-view-component.html

<app-finding
        *ngFor="let finding of findings | findingsFilter:filterDate:filterObject; let i = index"
        [finding]="finding"></app-finding>

main-view-component.ts

@Input() set filterObject(filterObject) {
    console.log('setter');
    this._filterObject = filterObject;
    this.filterDate = Date.now();
}

get filterObject() {
    console.log('getter');
    return this._filterObject;
}

private _filterObject = {};

public filterDate = Date.now();

首次显示页面时的控制台输出:

setter
getter
getter
getter
getter

我真的很想用“角度方式”来做,所以我想问的是我在这里想念的是什么?

3 个答案:

答案 0 :(得分:4)

可能是您的侧菜单组件发出了相同的对象。它的内容可以更改,但是发出的对象仍然相同。考虑改为发出对象的副本。 Object.assign({}, this.filterObject)可能会帮助您。

答案 1 :(得分:1)

这与生命周期的变更检测有关。没有Minimal, Complete and Verifiable Example,我无法真正告诉您如何解决它,但是我绝对可以告诉您如何解决它。

首先给您第二个组件一个模板变量,然后告诉您的输出更新其中的数据:

<aside>
     <app-article-view-aside [article]="article" (close)="closeModal($event)" (filter)="mainView.updateData($event)"></app-article-view-aside>
</aside>
<div class="articleContainerMainView">
     <app-article-view-main #mainView [article]="article" [filterObject]="filters"></app-article-view-main>
</div>

在第二个组件中,而不是使用管道,而是在函数中更新值:

updateData(event) {
  // original data is the unfiltered array of data
  this.findings = this.originalData.filter(...);
}

在您的模板中它变为

<app-finding
    *ngFor="let finding of findings; let i = index"
    [finding]="finding"></app-finding>

答案 2 :(得分:1)

问题是,当您第一次通过过滤器时,就给了它一个参考。因此,每次更改它时,角度的变化检测将永远不会检测到它,因为您没有更改滤镜对象或参考。

解决方案:我想您有两种解决方案: 1.每次更新过滤器时,都通过复制来更改其参考。 2.在您的管道中实现ngDoCheck,如果我没记错,即使您的引用仍然相同,它也会检测到此更改。