Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

remove from observableArray- knockoutjs

I'm sure this will be an easy answer for somebody. I have the following ViewModel:

@{
    var initialData = new JavaScriptSerializer().Serialize(Model);
}
var data = @Html.Raw(initialData);
function ViewModel(data) {
    var self = this;
    self.Name = ko.observable(data.Name);
    self.Items = ko.observableArray(data.Items);
    self.addItem = function() { self.Items.push(""); };
    self.removeItem = function(data) { self.Items.remove(data); }
}
$(document).ready(function() {ko.applyBindings(new ViewModel(data)); });

And the following View:

<div>
    Name: <span data-bind="text: Name"></span>
</div>
<div>
    Items: <button data-bind="click: addItem">Add Item</button>
</div>
<div>
    <table>
        <tbody data-bind="template: { name: 'itemTemplate', foreach: Items }"></tbody>
    </table>
</div>
<script type="text/html" id="itemTemplate">
    <tr>
        <td>
            <input data-bind="value: $data" />
            <a href="#" data-bind="click: function() {$parent.removeItem($data)}">Remove Item</a>
        </td>
    </tr>
</script>

Everything seems to work correctly except for removeItem. When new rows have been added, and "Remove Item" is clicked on an empty new row, all of the new rows will be removed with it. I've looked at tons of knockout tutorials trying to get this to work, and my method seems to be a valid attempt, but obviously... I must be missing something. Any suggestions?

like image 463
Mr Jones Avatar asked Aug 30 '12 16:08

Mr Jones


People also ask

How do I remove an item from an observable array?

pop() — Removes the last value from the array and returns it. unshift( value ) — Inserts a new item at the beginning of the array. shift() — Removes the first value from the array and returns it. reverse() — Reverses the order of the array and returns the observableArray (not the underlying array).

How do you remove the value from observable?

To trigger an element deletion, simply send an event on the subject: this. deleteSubject. next({op:'delete', id: '1'});

How do you update items in ObservableArray Knockout?

You should look at defining the object structure for each element of your array and then add elements of that type to your observable array. Once this is done, you will be able to do something like item. productQuantity(20) and the UI will update itself immediately.


1 Answers

The remove function of an observableArray loops through the array and deletes any items that match the value passed to it. In your case, you are just dealing with strings and it will see that all of the new ones (with no value) will match "".

There are a couple ways to handle it:

  • you can deal with objects instead of just strings like { value: ko.observable("") }. Then, when you pass $data it will only delete the actual item that matches that object reference. If your value is not observable and is the data itself (not a property), then your writes won't actually make it back to your view model.

  • if that does not work for your scenario, then you could delete items based on the index ($index) using splice.

I would probably do it like: http://jsfiddle.net/rniemeyer/N3JaW/

Also note that the event (click is a wrapper to event) binding will pass the current data as the first argument to the handler, so you can simplify your binding to click: $parent.removeItem.

Update: here are several ways that you can control how your object is converted to JSON:

  • ko.toJSON passes its 2nd and 3rd arguments on to JSON.stringify. The second arg let's you run a function to potentially replace values as described here. Here is a sample that checks to see if the key is a number (array item) and it has a value property. If so, it just returns the string rather than the object. http://jsfiddle.net/rniemeyer/N3JaW/1/

  • If you use a constructor function for your objects, then you can override the toJSON function as described here. Here is your sample with this functionality: http://jsfiddle.net/rniemeyer/N3JaW/2/

  • Another technique that can be used is to maintain a computed observable that has the "good" value. Here is sample: http://jsfiddle.net/rniemeyer/N3JaW/3/. In this one Items is a computed observable that returns the clean value. Items.asObjects contains the object version of your values. When converted to JSON, the asObjects part is naturally dropped when Items is converted to JSON. If you only need this "good" array when converting to JSON, then the other options are better for performance (they only are calculated when you want to send it).

like image 102
RP Niemeyer Avatar answered Sep 26 '22 00:09

RP Niemeyer