更新Knockout中的ViewModel

时间:2017-10-14 10:24:01

标签: knockout.js viewmodel

我是Knockout的新手。我正在尝试使用Knockout开发购物车功能。

我的问题是,当我想在购物车中放置计数器时,计数器将应用于所有购物车内容。

var FoodVM = function() {
  self.ID = ko.observable();
  self.Name = ko.observable();
  self.Price = ko.observable();
  self.Clicks = ko.observable(0);

  self.clickCounterAdd = function() {
    self.Clicks(self.Clicks() + 1);
  };
};

var FoodsListVM = function() {
  this.FoodsList = ko.observableArray([new FoodVM()]);
};

$(document).ready(function() {
  var model = new FoodsListVM();
  ko.applyBindings(model);

  var data = [{
      ID: 1,
      Name: "Test 1",
      Price: 25000
    },
    {
      ID: 2,
      Name: "Test 2",
      Price: 30000
    },
    {
      ID: 3,
      Name: "Test 3",
      Price: 35000
    }
  ];

  model.FoodsList(data);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<table border="1">
  <tr>
    <th>ID</th>
    <th>Name</th>
    <th>Price</th>
    <th>Add</th>
    <th>Count</th>
  </tr>
  <tbody data-bind="foreach: FoodsList">
    <tr>
      <td data-bind="text: ID"></td>
      <td data-bind="text: Name"></td>
      <td data-bind="text: Price"></td>
      <td>
        <button data-bind="click: clickCounterAdd" style="width: 73px;">Add</button>
      </td>
      <td>
        <input type="text" data-bind="value: Clicks" style="width: 20px;">
      </td>
    </tr>
</table>

JSFiddle演示。

你能告诉我我的错误在哪里吗?

2 个答案:

答案 0 :(得分:1)

您的第一个错误是忘记在self中定义FoodVM

var self = this;

因此,您将所有可观察对象和clickCounterAdd函数附加到全局范围内的self,而不是视图模型。

下一个错误是您使用单个FoodsList初始化FoodVM属性...

this.FoodsList = ko.observableArray([new FoodVM()]);

...但过了一会儿你用一个简单的JS对象data覆盖了它。

model.FoodsList(data);

这意味着一个FoodVM丢失了 - data中的项目不会自行成为FoodVM。需要某种循环才能将数据数组中的普通对象转换为FoodVM个实例。通常这是通过.map()电话完成的。

这是一种更好的viewmodel构建方法。

以这样的方式编写视图模型,使它们从参数对象(即从模型)初始化自己。使您的数据的属性名称与您的viewmodel的属性名称匹配。

function someViewmodel(params) {
    this.data = ko.observable(params.data);
}

在您的情况下,有两个视图模型,FoodFoodsList(我发现它们称为*VM是多余的,因此我没有这样做)。我已在模型中添加了一个级别(foods),以匹配FoodsList中同名的属性。

&#13;
&#13;
var Food = function(params) {
  var self = this;
  
  self.ID = ko.observable(params.ID);
  self.Name = ko.observable(params.Name);
  self.Price = ko.observable(params.Price);

  self.clicks = ko.observable(0);
  self.countClick = function () {
    self.clicks(self.clicks() + 1);
  };
};

var FoodsList = function(params) {
  var self = this; 

  self.foods = ko.observableArray(params.foods.map(function (item) {
    return new Food(item);
  }));
};


// ----------------------------------------------------------------------
var model = {
  foods: [
    { ID: 1, Name: "Test 1", Price: 25000 },
    { ID: 2, Name: "Test 2", Price: 30000 },
    { ID: 3, Name: "Test 3", Price: 35000 }
  ]
};

var vm = new FoodsList(model);
ko.applyBindings(vm);
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<table border="1">
  <tr>
    <th>ID</th>
    <th>Name</th>
    <th>Price</th>
    <th>Add</th>
    <th>Count</th>
  </tr>
  <tbody data-bind="foreach: foods">
    <tr>
      <td data-bind="text: ID"></td>
      <td data-bind="text: Name"></td>
      <td data-bind="text: Price"></td>
      <td>
        <button data-bind="click: countClick" style="width: 73px;">Add</button>
      </td>
      <td>
        <input type="text" data-bind="value: clicks" style="width: 20px;">
      </td>
    </tr>
</table>
&#13;
&#13;
&#13;

注意在初始化期间所有内容都是自引导的,new FoodsList(model)构建一个完整的嵌套视图模型。

顺便说一下,这个任务非常普遍,有一个淘汰插件可以为你完成:http://knockoutjs.com/documentation/plugins-mapping.html。花一些时间阅读其文档。

答案 1 :(得分:0)

查看下面的更改

var Food = function(params) {
  var self = this;

  self.ID = ko.observable(params.ID);
  self.Name = ko.observable(params.Name);
  self.Price = ko.observable(params.Price);

  self.clicks = ko.observable(0);
  self.countClick = function () {
    self.clicks(self.clicks() + 1);
  };
};

var FoodsList = function(params) {
  var self = this; 

  self.foods = ko.observableArray(params.foods.map(function (item) {
    return new Food(item);
  }));
};


// ----------------------------------------------------------------------
var model = {
  foods: [
    { ID: 1, Name: "Test 1", Price: 25000 },
    { ID: 2, Name: "Test 2", Price: 30000 },
    { ID: 3, Name: "Test 3", Price: 35000 }
  ]
};

var vm = new FoodsList(model);
ko.applyBindings(vm);