You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 09:55:20