如何将带有子可观察对象的函数绑定到可观察数组?

时间:2017-04-10 11:56:29

标签: javascript knockout.js

我有以下ViewModel:

var Order =  function(data) {
  this.fruits = ko.observableArray(ko.utils.arrayMap(data.Fruis, function(item) { return item; }));
  this.vegetables = ko.observableArray(ko.utils.arrayMap(data.Vegetables, function(item) { return item; }));
};

我需要定义绑定到特定实例的一些子属性和子可观察对象,以及水果和蔬菜的一些常用方法:

var Items =  function(data, type) {
  var self = this;
  self.type = type;
  self.choice = ko.observable(-1);
  self.choice.select = ko.computed(function(){
    var id = self.choice();
    // do stuff
  });
  self.choice.remove = function() {
    var id = self.choice.peek();
    // do stuff
  };
  self.add = function(code) {
    //do stuff
    self.choice(id);
  };
};

这是绑定包含我的方法集和子可观察对象的函数的正确方法,以便我可以使用如下方法:

orderViewModel.fruits.add("apples");
orderViewModel.fruits.add("bananas");
orderViewModel.fruits.choice(0);
orderViewModel.fruits.choice.remove();

console.log(ko.tpJSON(orderViewModel));
// prints: {fruits: [bananas], vegetables: []};

我认为没有必要使用扩展器,因为属性和方法不是通用的,并且不需要对所有可观察对象都是通用的。

我尝试从我的Item函数返回一个可观察数组,但由于子属性和子可观察性已经丢失,我无法使其工作。如何将Items绑定到我的可观察数组?

1 个答案:

答案 0 :(得分:3)

即使你可能不想创建一个扩展器,你在这里做的扩展一个可观察的数组......

如果您不想注册扩展器,可以创建一个小辅助函数来创建observableArray并在返回之前为其添加一些方法和属性。

在下面的示例中,您可以看到一些示例代码。一些重要的建议:

  • 如果您使用此方法,我建议不要覆盖observableArray中的默认方法。例如:remove默认采用一个项目;您希望它与外部choice索引一起使用...最好选择一个不同的名称,以便继续支持它们。
  • 如果你最终使用了很多扩展,那么创建一个在内部存储可观察数组的干净viewmodel可能是值得的。您可以定义toArray方法以导出到普通数组。

var obsCollection = function(initialItems) {
  
  var items = ko.observableArray(initialItems);
  
  items.choice = ko.observable(-1);
  items.add = items.push;
  
  var ogRemove = items.remove.bind(items);
  
  // I'd rename this to "deleteChoice"
  items.remove = function() {
    var index = items.choice();
    ogRemove(items()[index]);
    // Reset choice to -1 here?
  };
  
  
  return items;
};

var fruits = obsCollection(["Apple"]);

log(fruits);
fruits.add("Banana");
fruits.choice(0);
fruits.remove();
log(fruits);
fruits.remove();
fruits.add("Mango");
fruits.add("Lemon");
log(fruits);

function log(d) {
  console.log(JSON.stringify(ko.unwrap(d)));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

编辑以澄清({1}}使用(缺少)

由于我们不使用this关键字,因此无需使用new。在内部,this创建了一个observableArray实例,但我们引用此实例的唯一方法是通过new。从数组中分离原型方法时,我们需要确保使用itemsbind(或.call(items))在正确的上下文中调用它们。

如果您希望代码看起来像“类”,您可以执行:apply并继续使用var self = items;关键字,或重写代码以使用self关键字(我答案中的最后一点。

new
var myArray = ko.observableArray([1,2,3]);

try {
  // Reference the function without binding `this`:
  var removeFromMyArray = myArray.remove;

  // Internally, the observableArray.prototype.remove method
  // uses `this` to refer to itself. By the time we call it,
  // `this` will refer to `window`, resulting in an error.
  removeFromMyArray(2);
} catch(err) {
  console.log("ERROR:", err.message);
  console.log(myArray());
}


// By binding to the array, we ensure that the function reference
// is always called in the right context.
var boundRemoveFromMyArray = myArray.remove.bind(myArray);
boundRemoveFromMyArray(2);
console.log(myArray());