Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

add/delete items from Ember Data backed ArrayController

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?

like image 800
outside2344 Avatar asked Aug 10 '12 04:08

outside2344


2 Answers

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.

like image 94
outside2344 Avatar answered Sep 20 '22 11:09

outside2344


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();
},
like image 23
sly7_7 Avatar answered Sep 19 '22 11:09

sly7_7