Search code examples
knockout.jsknockout-es5-plugin

deleting items from an array with knockoutjs es5


Fiddle

I'm trying to use knockout.js with the ES5 plugin, but I'm not able to get deleting from an array to work.

It seems to work somewhat, you can delete, but there is always one item left in the array, but not really somehow. I'm totally confused why this doesn't work like you'd think it would.

What am I doing wrong?

(I have a more complex scenario that is using a durandal widget, but I was able to boil it down to just this, so I think the es5 plugin is the culprit)

Here is my markup:

<div data-bind="foreach: staffList" style="border:1px solid black;">
    <div style="border: 1px solid red;">
        <p data-bind="text: Name"></p>
        <p>
            <button data-bind="click: deleteClickHandler">Delete</button>
        </p>
    </div>
</div>

and script:

function ctor(){
  var self=this;
  self.staffList = [{Name:'one'},{Name:'two'},{Name:'three'},{Name:'four'}];
  ko.track(self.staffList, { deep: true });
  self.deleteClickHandler = function (obj) {
    //TODO show confirm dialog first
    var index = self.staffList.indexOf(obj);
    if (index >= 0) {
      self.staffList.splice(index, 1);
    }
  };
}
ko.applyBindings(ctor);

only other difference in the real world is that I'm getting the data from an API call, but the behavior is the same.


Solution

  • Interesting question. I'm not sure yet as to the root cause, but I did manage to find a way to make it work. Some changes I needed:

    • Make ctor a real Constructor function / use it as such;
    • Because of the previous point, you'll need to reference deleteClickHandler with specific scope;
    • Track self, not just the one member;

    Here's a sample that works for me:

    <div data-bind="foreach: staffList" style="border:1px solid black;">
        <div style="border: 1px solid red;">
            <span data-bind="text: Name"></span>
            <button data-bind="click: $root.deleteClickHandler">Delete</button>
        </div>
    </div>
    <hr>
    <pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
    
    function ViewModel(){
      var self = this;
      self.staffList = [{Name:'one'},{Name:'two'},{Name:'three'},{Name:'four'}];
      ko.track(self);
      self.deleteClickHandler = function (obj) {
        var index = self.staffList.indexOf(obj);
        if (index >= 0) {
          self.staffList.splice(index, 1);
        }
      };
    }
    ko.applyBindings(new ViewModel());
    

    In addition, I want ot mention that this is also possible:

      self.deleteClickHandler = function (obj) {
        self.staffList.remove(obj);
      };
    

    Again, I'm not sure why ko.track(self.staffList) didn't work, but the above at least gets you going.