添加/删除Ember Data支持的ArrayController中的项目

时间:2012-08-10 04:47:36

标签: ember.js ember-data

我在我的应用程序中使用ArrayController,它是通过应用程序的路由器从Ember Data REST调用中提供的:

postsController.connectOutlet('comment', App.Comment.find({post_id: post_id}));

对于Post UI,我可以添加/删除评论。当我这样做时,我希望能够通过删除或添加相同的元素来更新postsController的contentArray,以便为用户提供可视反馈,但是Ember Data并不好玩:

Uncaught Error: The result of a server query (on App.Comment) is immutable.

根据sly7_7的评论,我刚刚注意到,当没有查询(App.Comment.find())时,结果确实是DS.RecordArray,但是在有查询的情况下(App.Comment.find( {post_id:post_id}),返回DS.AdapterPopulatedRecordArray。

我是否必须.observes('contentArray')并创建一个可变副本?或者有更好的方法吗?

4 个答案:

答案 0 :(得分:7)

这是我最终实现的解决方案。正如问题中所提出的,我所知道的唯一解决方案是创建一个我通过添加和删除维护的内容的可变副本:

contentChanged: function() {
    var mutableComments = [];

    this.get('content').forEach(function(comment) {
        mutableComments.pushObject(comment);
    });

    this.set('currentComments', mutableComments);
}.observes('content', 'content.isLoaded'),

addComment: function(comment) {
    var i;
    var currentComments = this.get('currentComments');
    for (i = 0; i < this.get('currentComments.length'); i++) {
        if (currentComments[i].get('date') < comment.get('date')) {
            this.get('currentComments').insertAt(i, comment);
            return;
        }
    }

    // fell through --> add it to the end.
    this.get('currentComments').pushObject(comment);
},

removeComment: function(comment) {
    this.get('currentComments').forEach(function(item, i, currentComments) {
        if (item.get('id') == comment.get('id')) {
            currentComments.removeAt(i, 1);
        }
    });
}

然后在模板中绑定到这个计算属性:

{{#each comment in currentComments}}
    ...
{{/each}}

我对这个解决方案不满意 - 如果有更好的方法,我很乐意听到它。

答案 1 :(得分:2)

评论太长了......

我不知道你是如何尝试添加记录的,但你可以尝试这样做:App.Comment.createRecord({})。如果一切正常,它将自动更新您的控制器内容。 (我认为App.Comment.find()的结果是一个'实时'数组,当创建一个记录时,它会自动更新) 以下是我们在应用中执行此操作的方法:

App.ProjectsRoute = Ember.Route.extend({
  route: 'projects',

  collection: Ember.Route.extend({
    route: '/',

    connectOutlets: function (router) {
      router.get('applicationController').connectOutlet({
        name: 'projects',
        context: App.Project.find()
      });
    }
 })

然后,创建项目的处理程序(在路由器中):

createProject: function (router) {
  App.Project.createRecord({
    name: 'new project name'.loc()
  });
  router.get('store').commit();
},

答案 2 :(得分:0)

仅供记录:截至今天(使用Ember Data 1.0.0-beta),库将这种情况考虑在内。当删除数组中的记录时,将更新该数组。

如果您尝试手动删除该阵列上的元素,例如在包含控制器的模型上使用.removeObject(object_you_just_deleted)(这是一个ArrayController,因此它的模型是一个记录数组),您将获得像:

这样的错误

“服务器查询的结果(在XXXXX上 - 您尝试手动更新的模型)是不可变的”。

因此,不再需要手动编码从其所属的数组中删除记录。这是一个好消息,因为我觉得我喜欢使用ED并且一直在工作......:)

答案 3 :(得分:0)

<强>前言

我遇到了类似的问题,发现了一个棘手的解决方案。通过Ember-Data源代码和API文档运行,清除了AdapterPopulatedRecordArray从查询的查找请求返回的事实。那是什么手册说的:

  

AdapterPopulatedRecordArray表示记录的有序列表,其顺序和成员资格由适配器确定。例如,发送到适配器的查询可能会触发服务器上的搜索,其结果将加载到AdapterPopulatedRecordArray的实例中。

因此,不可变性的充分理由是这些数据由服务器控制。但如果我不需要呢?例如,我有一个包含许多任务的Tasklist模型,我在TasklistController中找到它们的方式,如

this.get('store').find('task',{tasklist_id: this.get('model').get('id')})

而且我还有一个大红色按钮“添加任务”,它必须创建并保存新记录,但我不想向服务器发出新的查找请求,以便重绘我的模板并显示新任务。对我来说,好的做法就像是

var task = this.store.createRecord('task', {
    id: Utils.generateGUID(),
    name: 'Lorem ipsum'
});
this.get('tasks').pushObject(task);

在那种情况下,我宣布错误。但是,嘿,我想喝酒开车!

<强>解决方案

DS.AdapterPopulatedRecordArray.reopen({
    replace: DS.RecordArray.replace
})

就是这样。一点点“靠我自己”的外观灵活性破解。