Ember Octane中Tracked属性变更时get函数未重新计算问题
问题原因分析
- 核心问题:@tracked对Ember可变数组的内部变更不敏感。@tracked追踪的是属性的引用变化,你用
A([])创建的Ember数组是可变对象,调用pushObject/removeObject只会修改数组内部内容,不会改变数组本身的引用,因此tracked无法感知到变化,导致availableAppDefinitions的getter不会重新计算。 - 尝试@computed时无效,是因为依赖键写法错误:只写
selectedAppCategories只会追踪数组引用的变化,要追踪数组内容的变更,必须加上.[]后缀,即'selectedAppCategories.[]'。
解决方案
两种推荐方案二选一即可:
方案一:原生JavaScript数组 + @tracked
将Ember数组替换为原生数组,通过替换整个数组的方式修改内容,让@tracked捕获到引用变化:
import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import Controller from '@ember/controller'; export default class IntegrationActivationIndexController extends Controller { @service session; @service router; @tracked selectedAppCategories = []; // 使用原生数组 get availableAppDefinitions() { let filteredAppCategories = this.selectedAppCategories; return this.model.app.filter(app => app.categories.some(appCategory => filteredAppCategories.includes(appCategory.name)) ); } @action onChangeCategory(category) { if (this.selectedAppCategories.includes(category)) { // 过滤得到新数组 this.selectedAppCategories = this.selectedAppCategories.filter(item => item !== category); } else { // 拼接得到新数组 this.selectedAppCategories = [...this.selectedAppCategories, category]; } } }
说明:原生数组的替换操作会改变selectedAppCategories的引用,@tracked能捕获到这个变化,从而触发getter重新计算。另外将原代码中filter+length的判断改为some,更高效(找到第一个匹配项就停止遍历)。
方案二:Ember数组 + @computed(修正依赖键)
如果坚持使用Ember数组,需要修正@computed的依赖键,确保追踪数组内容的变更:
import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { computed } from '@ember/object'; import Controller from '@ember/controller'; import { A } from '@ember/array'; export default class IntegrationActivationIndexController extends Controller { @service session; @service router; selectedAppCategories = A([]); // 无需@tracked,由computed追踪 @computed('selectedAppCategories.[]', 'model.app.[]') // 加.[]追踪内容变更 get availableAppDefinitions() { let filteredAppCategories = this.selectedAppCategories; return this.model.app.filter(app => app.categories.some(appCategory => filteredAppCategories.includes(appCategory.name)) ); } @action onChangeCategory(category) { if (this.selectedAppCategories.includes(category)) { this.selectedAppCategories.removeObject(category); } else { this.selectedAppCategories.pushObject(category); } } }
说明:selectedAppCategories.[]表示追踪数组的内容变更(新增、删除元素),model.app.[]确保当model中的app数组变化时也会重新计算,这样computed就能正确响应数组内部的修改。
额外模板修正
模板中input的value绑定有误,循环变量是appCategory,不是this.model.appCategory,同时建议给id加唯一后缀避免重复:
{{#each this.model.appCategories as |appCategory|}} <input id="checkbox_id_{{appCategory.name}}" value="{{appCategory.name}}" type="checkbox" onchange={{action (fn this.onChangeCategory) appCategory.name}}> {{/each}} <div> {{my-component value=availableAppDefinitions}} </div>
内容的提问来源于stack exchange,提问作者Pierre Girardeau
相关产品推荐
相关产品推荐

