从模板中隐藏已删除的记录

Hiding deleted records from templates

我想弄清楚是否有更好的方法来从模板中隐藏已删除的记录并将它们从计算属性中删除。我的 ember 应用程序正在侦听网络套接字,如果另一个用户删除了一条记录,则有一个处理程序可以从存储中删除该记录:

_handleDelete({id, type: modelName}) {
  const record = get(this, 'store').peekRecord(modelName, id);

  if (record) {
    get(this, 'store').deleteRecord(record);
  }
}

我目前正在做的是定义一个计算 属性 来过滤掉任何 isDeleted 为真的记录,并将其用于其他集合:

allItems: computed.filter('items.@each.isDeleted', function(item) {
  return !get(item, 'isDeleted');
}),

然后我使用这个计算 属性 来构建其他计算属性:

materialCosts: computed.filterBy('allItems', 'proposalCostType', 'material'),

这很好用,但我遇到的问题是我不是从这个开始的,所以我的很多模板都直接引用 hasMany 关系 (items),而不是过滤后的集合(allItems)。这是一个巨大的应用程序,因此我必须更新大量模板才能使其正常工作,而且我认为这不符合时间效率。

我从不希望删除的记录显示在模板中,也不希望任何计算属性能够引用它们,所以理想情况下我能够在 items 关系中做一些事情自动过滤掉已删除的记录并在删除时更新。

我正在使用 Ember Data 2.14,所以也许有更新的版本可以做到这一点? 更新 - 也在 2.18 上试过,看看它是否是我使用的版本。

编辑

我尝试了@Gennady Dogaev 在下面建议的卸载记录而不是删除记录的方法,但是 ember 给了我监视集合的计算属性的错误。比如这个:

approvableItems: computed('items.[]', function() {
  return get(this, 'items').filterBy('isApprovable');
}),

... 给我一个错误,即无法在未定义的对象上调用 isApprovable

您可以创建模板助手,然后将 {{#each items}} 包装成 {{#each (only-active items)}}

如果其他用户删除了记录,unload记录从存储中会更正确。之后模板将停止渲染它,就好像它从未出现在商店中一样。

我不建议在这种情况下使用 deleteRecord,因为它会弄脏 ember 存储中的数据。你应该在调用 deleteRecord 之后调用 record.save() 但它会向 API 发出 http 请求,我认为你不希望在描述的情况下。


在某些情况下,您可能会遇到神秘的 js 错误:

  1. 如果您尝试将与删除的 ID 相同的新记录推入存储
  2. 如果用户尝试重新访问试图加载 deleted/unloaded 记录的路由。这可以通过在浏览器
  3. 中使用 back/forward 按钮来实现

如果您在这些场景中遇到一些错误,请尝试使用以下方法卸载记录:

/**
 * This function unloads record from store and clears some private internals to avoid errors when UI tries to read
 * unloaded record from store
 *
 * @see https://discuss.emberjs.com/t/reusing-ids-for-soft-deleted-records/13715/6
 * @param store
 * @param model
 * @param id
 */
export default function(store, model, id) {
  let record, recordId, modelName;
  if (typeof model === 'object' && typeof model.get === 'function') {
    record = model;
    recordId = model.get('id') || id;
    modelName = model.constructor.modelName;
  } else {
    recordId = id;
    modelName = model;
    record = store.peekRecord(modelName, recordId);
  }
  if (record && !record.isDeleted) {
    store.unloadRecord(record);
  }
  if (recordId) {
    delete store.get('_identityMap')._map[modelName]._idToModel[recordId];
  }
}

我不知道它是否仍然需要在最新版本中,但我不久前需要这个代码。

您可以尝试结合内置帮助程序 unlessisDeleted 属性:

直接在您的视图中排除已删除的记录

在此处阅读有关 isDeleted 的更多信息:https://www.emberjs.com/api/ember-data/3.3/classes/DS.Model

app/routes/todos/index.js

import Ember from 'ember';

export default Ember.Route.extend(AuthenticatedRouteMixin, {
  model(params) {
    return Ember.RSVP.hash({
      todos: this.store.findAll('todos')
    });
  }
});

app/controllers/todos/index.js

export default Controller.extend({
  actions: {
    deleteMe(todo) {
      todo.deleteRecord();
      console.log('deleting todo ' + todo.id); // Just to see if the action works
    }
  }
});

app/templates/todos/index.hbs

{{#each model.todos as |todo|}}
  {{#unless todo.isDeleted}}
    <li>{{todo.title}} - <a {{action 'deleteMe' todo}}>delete me</a></li>
  {{/unless}}
{{/each}}

截图

您可以从 https://github.com/hernanvicente/todos-placeholder

克隆我的示例代码

一个简单的解决方案是做这样的事情。在你的 template.hbs 上做一个每个循环

  {{#each model as |todo|}}
    <li>{{todo.title}} - <a {{action 'removeRecord' todo}}>delete me</a></li>
  {{/each}}

如果您不想在删除记录后立即显示您的记录,只需执行以下操作即可。顺便说一句,下面是 ember-cli 1.13

引用的
removeRecord(todo) {
  todo.destroyRecord().then(() => {
    this.send('reloadModel');
  });
}

reloadModel 是在记录被删除后重新加载你的记录。但是,如果您的后端在您发送删除此类记录的请求后确实删除了该记录,那么一旦 removeRecord 被请求到您的 API,它就会删除您的数据。希望对您有所帮助。