Angular 输入 setter 只触发一次
Angular input setter triggering only once
我想在问题前说我已经阅读并理解其他可能重复的问题(例如 ),但无法完成这项工作,这让我相信问题有点不同本质上还是我误解了什么。
上下文: 我有一个 parent 组件和两个 child 组件(一个侧边菜单和一个主视图)。侧面菜单有一些复选框形式的过滤器,通过 [(ngModel)]
绑定到 object,并使用模板中的 (ngModelChange)="changeFilter()"
触发事件。
在 parent 组件中我捕获了事件:(filter)="updateFilters($event)"
。此函数更改 public 过滤器变量,然后我将其传递给另一个 child 组件,如下所示:[filterObject]="filters"
在主视图组件中,我将 filterObject 变量提供给管道以过滤 objects 的数组。
问题: 在我第一次更改过滤器后,管道只被调用一次。主视图中的 filterObject 变量在显示为 {{filterObject | json}}
时显示正确更新,但从未调用 setter。 setter 和 getter 中的控制台日志显示只有 getter 获得访问权限 (2-4)
我尝试过的解决方案:
向管道添加一个附加参数以触发它。这种方法的问题是,需要从 setter 为 filterObject
设置新日期,而 filterObject
不会被调用 - 如上所述
使管道不纯(可行但不是一个很好的解决方案,因为它使它处理非常大的复杂数组 objects 每次点击 2-4 次)
使 parent 更新过滤器变量,不是通过赋值运算符,而是通过 Object.assign
以更改引用并可能触发 setter
我正在考虑的解决方案:如果我无法弄清楚问题出在哪里,我正在考虑制作一个服务,通过观察者在两个组件之间进行通信.否则我可能不得不接受一个嵌入了一些自定义变化检测的不纯管道(我正在考虑将过滤器 object 转换为 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
我真的很想做 "The Angular Way" 所以我的问题是我在这里缺少什么?
这与你生命周期的变化检测有关。没有 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>
可能是您的侧边菜单组件发出了相同的对象。它的内容可能会改变,但发出的对象仍然是一样的。考虑改为发出对象的副本。 Object.assign({}, this.filterObject)
可能对你有帮助。
问题是当你第一次通过过滤器时,你给了它一个参考。因此,每次更改它时,angular 的更改检测将永远不会检测到它,因为您没有更改过滤器对象或引用。
解决方案:我猜你有两个解决方案:
1. 每次更新过滤器时,通过复制更改其引用。
2. 在你的管道中实现 ngDoCheck,如果我没记错的话它会检测到这个变化,即使你的引用仍然相同。
我想在问题前说我已经阅读并理解其他可能重复的问题(例如
上下文: 我有一个 parent 组件和两个 child 组件(一个侧边菜单和一个主视图)。侧面菜单有一些复选框形式的过滤器,通过 [(ngModel)]
绑定到 object,并使用模板中的 (ngModelChange)="changeFilter()"
触发事件。
在 parent 组件中我捕获了事件:(filter)="updateFilters($event)"
。此函数更改 public 过滤器变量,然后我将其传递给另一个 child 组件,如下所示:[filterObject]="filters"
在主视图组件中,我将 filterObject 变量提供给管道以过滤 objects 的数组。
问题: 在我第一次更改过滤器后,管道只被调用一次。主视图中的 filterObject 变量在显示为 {{filterObject | json}}
时显示正确更新,但从未调用 setter。 setter 和 getter 中的控制台日志显示只有 getter 获得访问权限 (2-4)
我尝试过的解决方案:
向管道添加一个附加参数以触发它。这种方法的问题是,需要从 setter 为
filterObject
设置新日期,而filterObject
不会被调用 - 如上所述使管道不纯(可行但不是一个很好的解决方案,因为它使它处理非常大的复杂数组 objects 每次点击 2-4 次)
使 parent 更新过滤器变量,不是通过赋值运算符,而是通过
Object.assign
以更改引用并可能触发 setter
我正在考虑的解决方案:如果我无法弄清楚问题出在哪里,我正在考虑制作一个服务,通过观察者在两个组件之间进行通信.否则我可能不得不接受一个嵌入了一些自定义变化检测的不纯管道(我正在考虑将过滤器 object 转换为 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
我真的很想做 "The Angular Way" 所以我的问题是我在这里缺少什么?
这与你生命周期的变化检测有关。没有 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>
可能是您的侧边菜单组件发出了相同的对象。它的内容可能会改变,但发出的对象仍然是一样的。考虑改为发出对象的副本。 Object.assign({}, this.filterObject)
可能对你有帮助。
问题是当你第一次通过过滤器时,你给了它一个参考。因此,每次更改它时,angular 的更改检测将永远不会检测到它,因为您没有更改过滤器对象或引用。
解决方案:我猜你有两个解决方案: 1. 每次更新过滤器时,通过复制更改其引用。 2. 在你的管道中实现 ngDoCheck,如果我没记错的话它会检测到这个变化,即使你的引用仍然相同。