Ember中sort计算属性自定义排序无法自动更新问题
自定义排序计算属性在模型新增时无法自动更新的解决办法
环境版本
- Ember-Source: 4.7.0
- Ember-Data: 4.7.3
- Ember-CLI: 4.6.0
- Ember-CLI-Typescript: 2.4.0
问题描述
我尝试用sort计算属性给模型做自定义排序,页面初始加载时排序正常,但当有新模型存入store后,页面无法自动更新。测试时第一个断言通过,但第二个断言失败:
actual: > 2:0
expected: > 2:2
我试过把@sort("model", sortNumeric)改成model.[]、model.@each、model.@each.id等组合,都没用。请问怎么实现自定义排序的自动更新?这是不是相关GitHub问题的复现?
相关代码
app/models/item.ts
import Model, { attr } from '@ember-data/model'; export default class Item extends Model { @attr('string') declare name: string; } // DO NOT DELETE: this is how TypeScript knows how to look up your models. declare module 'ember-data/types/registries/model' { export default interface ModelRegistry { 'item': Item; } }
app/routes/index.ts
import Route from '@ember/routing/route'; import { service } from '@ember/service'; import type Store from '@ember-data/store'; export default class Index extends Route { @service declare store : Store; async model(){ return this.store.peekAll('item'); } }
app/controllers/index.ts
import Controller from '@ember/controller'; import { sort } from '@ember/object/computed'; import Item from 'sorting-test/models/item'; const sortNumeric = (a: Item, b: Item) => parseInt(a.id, 10) - parseInt(b.id, 10); export default class Index extends Controller { @sort("model", sortNumeric) declare sortedModels: Item[] } // DO NOT DELETE: this is how TypeScript knows how to look up your controllers. declare module '@ember/controller' { interface Registry { 'index': Index; } }
app/templates/index.hbs
{{@model.length}}:{{this.sortedModels.length}}
tests/acceptance/index-test.ts
import { module, test } from 'qunit'; import { visit, settled } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; import type Store from '@ember-data/store'; module('Acceptance | index', function (hooks) { setupApplicationTest(hooks); test('visiting /', async function (assert) { const store = this.owner.lookup('service:store') as Store; await visit('/'); assert.equal(this.element.textContent, '0:0'); store.createRecord('item', {id: '10', name: 'A'}); store.createRecord('item', {id: '2', name: 'B'}); await settled(); assert.equal(this.element.textContent, '2:2'); }); });
解决方案
问题根源是@sort计算属性在处理Ember Data的RecordArray时,无法正确监听数组内容的变更(对应你提到的GitHub问题)。可以改用以下两种方式实现自动更新:
方案1:手动定义计算属性
用@computed('model.[]')替代@sort,手动实现排序逻辑,确保监听RecordArray的内容变更:
import Controller from '@ember/controller'; import { computed } from '@ember/object'; import Item from 'sorting-test/models/item'; const sortNumeric = (a: Item, b: Item) => parseInt(a.id, 10) - parseInt(b.id, 10); export default class Index extends Controller { @computed('model.[]') get sortedModels() { // 将RecordArray转为普通数组后再排序 return [...this.model].sort(sortNumeric); } }
方案2:使用Tracked属性+观察者
如果偏好Ember推荐的tracked特性,可通过监听RecordArray的变更来更新排序结果:
import Controller from '@ember/controller'; import { tracked } from '@glimmer/tracking'; import { addObserver, removeObserver } from '@ember/object'; import Item from 'sorting-test/models/item'; const sortNumeric = (a: Item, b: Item) => parseInt(a.id, 10) - parseInt(b.id, 10); export default class Index extends Controller { @tracked sortedModels: Item[] = []; init() { super.init(); this.updateSortedModels(); // 监听RecordArray的内容变更 addObserver(this.model, '[]', this, 'updateSortedModels'); } willDestroy() { super.willDestroy(); // 组件销毁时移除观察者,避免内存泄漏 removeObserver(this.model, '[]', this, 'updateSortedModels'); } updateSortedModels() { this.sortedModels = [...this.model].sort(sortNumeric); } }
两种方案都能让sortedModels在RecordArray新增/删除模型时自动重新排序并更新页面。
内容的提问来源于stack exchange,提问作者MT0
相关产品推荐
相关产品推荐

