add/delete items from Ember Data backed ArrayController

匿名 (未验证) 提交于 2019-12-03 02:30:02

问题:

I'm using an ArrayController in my application that is fed from a Ember Data REST call via the application's Router:

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

For the Post UI, I have the ability to add/remove Comments. When I do this, I'd like to be able to update the contentArray of the postsController by deleting or adding the same element to give the user visual feedback, but Ember Data is no fun:

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

Per sly7_7's comment below, I just noticed that the result is indeed DS.RecordArray when there is no query (App.Comment.find()), but in the case where there is a query (App.Comment.find({post_id: post_id}), a DS.AdapterPopulatedRecordArray is returned.

Do I have to .observes('contentArray') and create a mutable copy? Or is there a better way of doing this?

回答1:

Here is what I ended up implementing to solve this. As proposed in the question, the only solution I know about is to create a mutable copy of the content that I maintain through add and deletes:

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);         }     }); } 

Then in the template, bind to the this computed property:

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

I'm not satisfied with this solution - if there is a better way to do it, I'd love to hear about it.



回答2:

A comment will be too long...

I don't know how do you try to add a record, but you can try to do this: App.Comment.createRecord({}). If all goes right, it will update automatically your controller content. (I think the result of App.Comment.find() works as a 'live' array, and when creating a record, it's automatically updated) Here is how we do this in our app:

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()       });     }  }) 

and then, the handler of creating a project (in the router):

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


回答3:

Just for the record: as of today (using Ember Data 1.0.0-beta), the library takes this situation into account. When a record in an array gets deleted, the array will be updated.

If you try to delete an element on that array manually, for example by using .removeObject(object_you_just_deleted) on the model of the containing controller (which is an ArrayController, hence its model an array of records), you'll get an error like:

"The result of a server query (on XXXXX - the model you try to update manually) is immutable".

So there is no need anymore to code by hand the deletion of the record from the array to which it belonged. Which is great news because I felt like using ED and working it around all the time... :)



回答4:

Foreword

I had a similar problem and found a little tricky solution. Running through the Ember-Data source code and API docs cleared for me the fact that AdapterPopulatedRecordArray returns from the queried find requests. Thats what manual says:

AdapterPopulatedRecordArray represents an ordered list of records whose order and membership is determined by the adapter. For example, a query sent to the adapter may trigger a search on the server, whose results would be loaded into an instance of the AdapterPopulatedRecordArray.

So the good reason for immutability is that this data is controlled by the server. But what if I dont need that? For example I have a Tasklist model with a number of Tasks and I find them in a TasklistController in a way like

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

And also I have a big-red-button "Add Task" which must create and save a new record but I dont want to make a new find request to server in order to redraw my template and show the new task. Good practice for me will be something like

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

In that case I got announced error. But hey, I want to drink-and-drive!

Solution

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

So that's it. A little "on my own" ember flexibility hack.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!