如何处理绑定:更改 observableArray 中的数据后的 foreach

How to process binding : foreach after altering data in observableArray

我正在改变一个 observableArray,修改订阅事件中的一些数据。首先,我使用 ko.toJS() 转换 ObservableArray,映射数据并进行更改。最后我调用 self.menuCategories(jsArray) 再次设置 observableArray。

似乎我以某种方式失去了与 observableArray 的“连接”,因为我代码中的 foreach 语句突然中断。

要么有更简单的方法来处理这个问题,要么我没有正确处理 observables。

代码:

var MenuWizardModel = function() {
   var self = this;
   self.menuCategories = ko.observableArray();
   self.commonDiscount = ko.observable(0);

   // Handling adding items to menuCategories.
   self.addNewSubMenuItem = function () {
        var newSubMenuItem = new SubMenuItemViewModel(self.newSubMenuItemName(), []);
        self.menuCategories.push(newSubMenuItem);
        self.newSubMenuItemName(null);
        self.createNewSubMenu(false);
    }

  function SubMenuItemViewModel(name, foodItemList) {
       var self = this;
       self.name = ko.observable(name);
       self.foodItemList = ko.observableArray(foodItemList);
  }

   self.commonDiscount.subscribe(function(val) {
        var discount = parseInt(val) / 100;
        var jsArray = ko.toJS(self.menuCategories);
        console.log(jsArray)
        jsArray = ko.toJS(jsonArray[0].foodItemList.map(item => {
            item.price = parseInt(item.price) - (parseInt(item.price) * discount);
            return item;
        }));
        self.menuCategories(jsArray);
    });

标记:

 <div data-bind="foreach: menuCategories">
          <h4 data-bind="text: name"></h4>
          <div data-bind="foreach: foodItemList" class="list-group">
          ...

数据:

我认为处理此类事情的最佳方法是向捕获全局折扣并计算折扣价格的食品添加计算可观察值。

类似于以下内容。

var MenuWizardModel = function() {
  var self = this;
  self.menuCategories = ko.observableArray([{
    name: 'Main Meals'
  }]);
  self.commonDiscount = ko.observable(0);
  self.newSubMenuItemName = ko.observable();
  
  // Handling adding items to menuCategories.
  self.addNewSubMenuItem = function() {
    var newSubMenuItem = new SubMenuItemViewModel(self.newSubMenuItemName(), [{name: 'Oranges', price: 3.99}]);
    self.menuCategories.push(newSubMenuItem);
    self.newSubMenuItemName(null);
    //self.createNewSubMenu(false);
  }
  function mapFoodItem(item){
    item.discountedPrice= ko.pureComputed(function(){
      var discount = parseInt(self.commonDiscount()) / 100
      return parseInt(item.price) - (parseInt(item.price) * discount);
    });
    return item;
  }
  
  function SubMenuItemViewModel(name, foodItemList) {
    var self = this;
    self.name = ko.observable(name);
    self.foodItemList = ko.observableArray(foodItemList.map(mapFoodItem));
  }
};

ko.applyBindings(new MenuWizardModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<label>Discount <input data-bind="value: commonDiscount"></label>
<label>Sub Menu Name: <input data-bind="value: newSubMenuItemName" /></label>
<button data-bind="click: addNewSubMenuItem">Add Sub Menu</button>

<div data-bind="foreach: {data: menuCategories, as: 'menu' }">
  <h4 data-bind="text: menu.name"></h4>
  <div data-bind="foreach: {data: menu.foodItemList, as: 'food'}" class="list-group">
    <div class="list-group-item">
      Name: <span data-bind="text: food.name"></span> 
      Price: <span data-bind="text: food.price"></span>
      Discounted Price: <span data-bind="text: food.discountedPrice"></span>
    </div>
  </div>
</div>