Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ember.js: Deleting an Item from a Sorted ArrayController

In the Ember app I'm building, I've got an ArrayController managing a list of items with several columns of data for each record object in the array with a sort button in each column header in the view. I have set up the list to sort on a given column per Balint Erdi's recommended method here. You will see this sorting in my code below.

The sorting works fine. However, the problem arises when I remove an item from the array. Currently, when I attempt to remove an item from the array, the correct item is apparently removed from the array and is properly deleted from the store and the delete is saved to my backend. However, after the item removal, my view is not correct. In some cases, the wrong item is shown as removed, in other cases, no item is shown as removed. Yet IF I press sort again, the view is updated correctly.

So, the index of the array is obviously getting off some how, but I'm not sure how and all of my attempts to apply the tricks of others are not working!

Here is my route object:

  App.UsersFilesRoute = Ember.Route.extend({
    model: function () {
      return this.modelFor('users').get('files');
      }
  });

Here is my ArrayController:

  App.UsersFilesController = Ember.ArrayController.extend({
    sortProperties: ['name'],
    sortedFiles: Ember.computed.sort('model', 'sortProperties'),
    actions: {
      addFile: function(file) {
        var newFile = this.store.createRecord('file', {
          name: file.name.trim(),
          fileSize: file.size,
          loaded: false
        });
        this.pushObject(newFile);
      },
      sortBy: function (sortProperties) {
        this.set('sortProperties', [sortProperties]);
      },
      removeFile: function (fileToRemove) {
        var _this = this;
        var file = this.store.find('file', fileToRemove.get('id'));
        file.then( function (file) {
          _this.removeObject(file);
          file.deleteRecord();
          file.save();
        });
      },
      saveFile: function (file) {
        ....
      }
    }
  });

And here is my template code:

  <div class="hidden-xs row user-file-header-row">
    <div class="col-sm-5 col-md-5 user-file-header">
      File Name
      <button type="button"  class="btn-xs btn-default files-sort-btn" {{ action 'sortBy' 'name'}}></button>
    </div>
    <div class="col-sm-1 col-md-1 user-file-header">
      Size
      <button type="button"  class="btn-xs btn-default files-sort-btn" {{ action 'sortBy' 'fileSize'}}></button>
    </div>
  </div>
  {{#each file in sortedFiles}}
    <div class="row user-file user-file-break">
      <div class="col-xs-11 col-sm-5 col-md-5  user-file-name">
        <a {{ bind-attr href="file.path" }}  >{{ file.name }} </a>
      </div>
      <div class="col-xs-9 col-sm-1 col-md-1">
        {{ format-file-size file.fileSize }}
      </div>
      <div class="col-xs-9 col-sm-1 col-md-1">
        <button type="button"  class="btn-xs btn-default files-list-btn" {{ action 'removeFile' file }}></button>
      </div>
    </div>
  {{/each}}

NOTE: There is some similarity between my question and this other StackOverflow question: After using jQuery UI to sort an Ember.js item, using Ember Data's model.deleteRecord() doesn't work, however, I've attempted to apply that answer my own problem with no success. Furthermore, I have no jQuery going on here in my sorting.

like image 752
Chad Moore Avatar asked Nov 01 '22 22:11

Chad Moore


1 Answers

OK. I have found an answer, or rather an answer has found me.

My problem was that in the code above I was removing the itemfrom the ArrayController and then calling .delete() and .save(). This sequences of calls was sending conflicting signals to Ember on how to update my view. Apparently, the .removeObject() was actually removing the item from the array, but then the subsequent .delete()/.save() was setting the model behind the view to a state just before deletion (not sure about that but that's what I saw happening).

So anyways, .destroyRecord() returns a promise, so I moved the .removeObject() within the .then() for the promise, and that resolves the issue.

So, the following code in the removeFile action resolved the issue:

removeFile: function () {
  var self = this;
  var fileToRemove = this.get('fileToRemove');
  var file = this.store.find('file', fileToRemove.get('id'));
  file.then (function (file) {
    file.destroyRecord().then(function(){
      self.get('model').removeObject(file);
    });
  });
}

Note that you don't have to do the this.store.find() first, you could simply do the following:

removeFile: function () {
  var self = this;
  var fileToRemove = this.get('fileToRemove');
  fileToRemove .destroyRecord().then(function(){
    self.get('model').removeObject(file);
  });
}

However, I chose to be conservative and double-check the store. That seems safer to me.

like image 69
Chad Moore Avatar answered Nov 12 '22 12:11

Chad Moore