Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

KnockoutJS - Observable Array of Observable objects

I would like to display an editable list of items, each item of which is editable (kind of like an editable grid, in a way). I am using KnockoutJS. I cannot use just a simple Observable Array because, as the documentation states "An observableArray tracks which objects are in the array, not the state of those objects"

So, I have created an observableArray of observable objects (using utils.arrayMap), and bound them to the view. However, the problem is that if I edit the data on screen, any data-entry changes that the user makes on screen do not appear to take effect. See http://jsfiddle.net/AndyThomas/E7xPM/

What am I doing wrong?

<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/2.0.0/knockout-min.js" type="text/javascript"></script>  <table>    <tbody data-bind="template: { name:'productListJavascriptTemplate', foreach: products}">    </tbody> </table>   <script type="text/html" id="productListJavascriptTemplate"> <tr>     <td>Name: <input data-bind="value: Name"/></td>     <td>Name: <span data-bind="text: Name"/></td>     <td><select data-bind="options: this.viewModel.categories,          optionsText: 'Name', optionsValue: 'Id', value: CategoryId,         optionsCaption: 'Please select...'"></select></td>     <td>CategoryId: <input data-bind="value: CategoryId"/></td>  </tr>  </script>​  var categoryList= [ {    Name: "Electronics",    Id: "1"}, {    Name: "Groceries",    Id: "2"} ];  var initialData= [ {    Name: "Television",    CategoryId: "1"}, {    Name: "Melon",    CategoryId: "2"} ];  var viewModel = {     products: ko.observableArray(         ko.utils.arrayMap(initialData, function(product) {                                  return ko.observable(product);          })),     categories: ko.observableArray(categoryList)        };   $(function() {     ko.applyBindings(viewModel);  }); 

like image 254
Andy Thomas Avatar asked Apr 06 '12 19:04

Andy Thomas


People also ask

How do you read an observable array?

Reading information from an observableArray So, you can get the underlying JavaScript array by invoking the observableArray as a function with no parameters, just like any other observable. Then you can read information from that underlying array. For example, alert('The length of the array is ' + myObservableArray().

How do you declare an observable array?

If you want to declare the variable as the observable itself, you simply assign it as the call: this. listeIsolements$ = this.


2 Answers

ko.utils.arrayMap doesn't map your viewmodel's properties as observables, and that's why you don't see them updated dynamically.

If you define your CategoryId as an observable, you'll see it update as expected:

var initialData = [     {         Name: "Television",         CategoryId: ko.observable("1")     },     {         Name: "Melon",         CategoryId: ko.observable("2")     } ]; 

See this updated jsfiddle: http://jsfiddle.net/tuando/E7xPM/5/

like image 184
Tuan Avatar answered Sep 23 '22 14:09

Tuan


To follow up on Tuan's answer, I needed to populate my objects based on data returned from a server method from an ASP.Net MVC controller, where the list of Products is contained in the view's Model, and the list of categories for the drop down box is in the ViewBag. I used the following code (see also http://www.knockmeout.net/2011/04/utility-functions-in-knockoutjs.html):

var initialData = @Html.Raw( new JavaScriptSerializer().Serialize(Model)); var categoryList = @Html.Raw( new JavaScriptSerializer().Serialize(ViewBag.CategoryList));  var ObservableProduct = function(name, description, categoryId) {              this.Name = ko.observable(name);              this.Description = ko.observable(description);     this.CategoryId = ko.observable(categoryId); };    var viewModel = {     products: ko.observableArray(ko.utils.arrayMap(initialData, function(product) {              return new ObservableProduct(product.Name, product.Description, product.CategoryId);          })),     categories: ko.observableArray(categoryList)        };  $(function() {     ko.applyBindings(viewModel);  }); 

Thanks, Tuan!

like image 40
Andy Thomas Avatar answered Sep 25 '22 14:09

Andy Thomas