KnockoutJS - Observable Array of Observable objects

笑着哭i 提交于 2019-11-27 02:04:09

问题


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);

});


回答1:


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/




回答2:


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!




回答3:


I am using writable computed observables that are initialized in the call to ko.utils.arrayMap

May be an overkill in your case, but it might help somebody else. See this jsFiddle sample

  // Writeable computed observables
  function Customer(id, firstName, lastName, preferred) {
     var self = this;
     self.id = id;
     self.firstName = firstName;
     self.lastName = lastName;
     // Non-Writeable computed observable
     self.fullName = ko.computed(function() {
        var fn = self.firstName;
        var ln = self.lastName;
        return ln ?  fn + ' ' + ln : fn;
     }, self);
     self.preferred = ko.observable(preferred);
     // Writeable computed observable
     self.isPreferred = ko.computed({
        read: function() {
           var preferredStr = self.preferred() || '';
           var isPreferredComputed = preferredStr.toUpperCase();
           return (isPreferredComputed === 'Y') ?  true : false;
        },
        write: function(value) {
           self.preferred( (!value) ? '' : (value ? 'Y' : ''));            
        },
        owner: self        
    });    
 }
  var mappedData = ko.utils.arrayMap(dataFromServer, function(customer) {
      return new Customer(customer.id, customer.firstName, customer.lastName, customer.preferred);
  });


来源:https://stackoverflow.com/questions/10048553/knockoutjs-observable-array-of-observable-objects

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!