0

I'm trying to get my head around observables in knockout js! However, I'm facing a problem implementing the remove function on an observable array.

My js is as follow:

$(function () {
    var data = [{ name: "name1" }, { name: "name2" }, { name: "name3"}];
    var viewModel = {
        namesList: ko.observableArray(data),
        nameToAdd: ko.observable("name4"),
        myCustomAddItem: function () {
            this.namesList.push({ name: this.nameToAdd() });
        },
        myCustomRemove: function () {
            console.log("before + " + this.nameToAdd());
            this.namesList.remove(this.nameToAdd());
            console.log("after + " + this.nameToAdd());
        }
    };
    ko.applyBindings(viewModel);
});

and my html is:

Name To add/remove <input type="text" data-bind="value: nameToAdd, valueUpdate: 'afterkeydown'"/>
<ul data-bind="template: {name: 'listTemp1', foreach :namesList}">

</ul>
<p>
    <button data-bind="click: myCustomAddItem">Add Item</button>
    <button data-bind="click: myCustomRemove">Remove Item</button>

    <script id="listTemp1" type="text/html">
        <li data-bind="text:name"> </li>
    </script>
</p>

my myCustomAddItem works fine, but not the myCustomRemove. I also have put a console.log before and after the this.namesList.remove(this.nameToAdd()); to see if anything's wrong there, but I cannot see any error in there. When I click the "Remove Item" button, firebug console shows the logs but the item's not removed from the list.

Any help appreciated

Amir
  • 9,577
  • 11
  • 41
  • 58

3 Answers3

8

The parameter to remove should be a function which returns true or false on whether to remove something.

It works quite similarly to the filter function.

In your case, something like this should work:

myCustomRemove: function () {
    console.log("before + " + this.nameToAdd());

    var nameToAdd = this.nameToAdd();
    this.namesList.remove(function(item) {
        //'item' will be one of the items in the array,
        //thus we compare the name property of it to the value we want to remove
        return item.name == nameToAdd;
    });

    console.log("after + " + this.nameToAdd());
}
Jani Hartikainen
  • 42,745
  • 10
  • 68
  • 86
3

[this should be a comment on Jani answer, but I can't still comment on others post, sorry] Just a small clarification: technically you can call remove() passing the element to remove, see section "remove and removeAll" on http://knockoutjs.com/documentation/observableArrays.html.

the problem with your code is that the elements in 'data' array are objects (containing a property called 'name'), and you are asking to remove from the array the string "name4" (or whatever 'nameToAdd' contains).

You can be tempted to create a new object to pass to remove, like this:

// old code
//this.namesList.remove(this.nameToAdd());
this.namesList.remove({ name: this.nameToAdd() });

but this still fails, because the way javascript object equality works (see, for example: How to determine equality for two JavaScript objects?).

So, in the end you need to use the function anyway.

In this simple example, you can also convert the 'namesList' array to a simple array of strings, and bind "$data" in the template. see http://jsfiddle.net/saurus/usKwA/. In a more complex scenario, maybe you can't avoid using objects.

Community
  • 1
  • 1
saurus
  • 191
  • 1
  • 3
  • 10
  • Thanks for your detailed comment.It really helps understanding what's going on now :) – Amir Dec 30 '11 at 22:13
0

[observableArray].remove(function(item) { return item.[whatever] == [somevalue] ; } );

jwize
  • 4,230
  • 1
  • 33
  • 51