Remove from observableArray-knockoutjs

I am sure this will be an easy answer for someone. 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 works correctly except removeItem . When new lines are added and “Delete item” is clicked on an empty new line, all new lines will be deleted along with it. I looked at tons of knockout tutorials, trying to get this to work, and my method seems like a real attempt, but obviously ... I have to miss something. Any suggestions?

+6
source share
1 answer

The remove function of the observed Array sings through the array and removes any elements that match the value passed to it. In your case, you are simply dealing with strings, and he will see that all new (without value) will match "".

There are several ways to handle this:

  • you can process objects, not just strings, such as { value: ko.observable("") } . Then, when you go through $data , it will only remove the actual item corresponding to this object reference. If your value is not observable, but the data itself (and not the property), then your records will not actually return to your view model.

  • if this does not work for your scenario, you can remove the elements based on the index ( $index ) using splicing.

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

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

Update : several ways to control how your object is converted to JSON:

  • ko.toJSON passes its 2nd and 3rd arguments to JSON.stringify . The second arg allows you to run the function to potentially replace the values, as described here . Here is an example that checks if a key is a number (an element of an array) and has a value property. If so, it just returns a string, not an object. http://jsfiddle.net/rniemeyer/N3JaW/1/

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

  • Another method that can be used is to maintain a computed observable that has a “good” value. Here is an example: http://jsfiddle.net/rniemeyer/N3JaW/3/ . In this case, Items is a computed observable that returns a pure value. Items.asObjects contains the object version of your values. When converting to JSON, the asObjects part asObjects naturally discarded when Items converted to JSON. If you need only this “good” array when converting to JSON, then other parameters are better for performance (they are calculated only when you want to send it).

+14
source

Source: https://habr.com/ru/post/924186/


All Articles