官术网_书友最值得收藏!

  • Mastering KnockoutJS
  • Timothy Moran
  • 747字
  • 2021-08-05 17:13:12

Defining viewmodels

Viewmodels are the objects whose properties your view binds with; they form the binding context. It is the representation of your data and operations for your view (we will cover them in detail in the Control flow bindings section later in this chapter). Like regular objects in JavaScript, there are many ways to actually create them, but Knockout introduces some specific challenges.

The this and self keywords

In JavaScript, this has a special meaning; it refers to the object calling the function. Functions called from an object get that object set to this. However, for functions that are anonymously called by code, that is merely the inside of an object, the behavior is different. Consider the following viewmodel:

function Invoice() {
  this.subtotal = ko.observable();
  this.total = ko.computed(function() {
  return this.subtotal() * 1.08; //Tax Rate
  });
}

The function inside the computed observable is not a property of the Invoice object. As it runs in a different context, its value for this will be the window object, not the Invoice object. It will not be able to find the subtotal property. There are two ways to handle this.

The first is by using the second parameter of the ko.computed function to bind the function to this:

function Invoice() {
  this.subtotal = ko.observable();
  this.total = ko.computed(function() {
    return this.subtotal() * 1.08; //Tax Rate
  }, this);
}

This gives the computed observable a reference to the Invoice that originally defined it, which allows the computed observable to call the supplied function in the correct context.

The second way to ensure the computed observable can reference the subtotal, is to capture the value of this in a closure. You can then use the closure to safely refer to the properties of the parent viewmodel. There are several conventional names for such a closure: that, _this, or self.

I prefer to use self as it is visually distinct from this while still carrying a similar meaning, but it's up to you:

function Invoice() {
  var self = this;
  self.subtotal = ko.observable();
  self.total = ko.computed(function() {
return self.subtotal() * 1.08; //Tax Rate
  });
}

I find the second method easier to remember. If you always use self to refer to the model, it will always work. If you have another anonymous function inside the computed, you will have to remember to bind that function as well; self continues to work as a closure no matter how many levels deep you nest. The self variable works as a closure inside any function defined in your viewmodel, including subscriptions. It's also easier to spot when self isn't being used, which is very helpful while debugging your code.

Problems with prototypes

If you are working with viewmodels that will be inherited by other viewmodels, you might think that putting all the base observable properties on the prototype is the way to go. In vanilla JavaScript, if you are inheriting an object, try to change the value of a property stored on the prototype; the property would be added to the inheriting object leaving the prototype intact. When using observables in Knockout though, this isn't the case. The observables are functions, and their values are set by calling them with a single parameter, not by assigning new values to them. Because prototypical inheritance would result in multiple objects referring to a single observable; observables cannot be safely placed on viewmodel prototypes. Nonobservable functions can still be safely included in prototypes. For example, consider the following objects:

var protoVm = {
  name: ko.observable('New User')
};

var base1 = Object.create(protoVm);
var base2 = Object.create(protoVm);

base2.name("Base2");

The last line will cause the name of both objects to be updated, as it is referring to the same function. This example can be seen in the cp1-prototype branch, which includes two input elements bound to the name of each viewmodel. As they are really the same observable, changing one will affect the other.

Serializing viewmodels

When you are ready to send your viewmodels to the server, or really do anything that requires you to work with their values instead of observables, Knockout provides two very handy utility methods:

  • ko.toJS: This function takes an object and does a deep copy, unwrapping all observables, into a new JavaScript object whose properties are normal (nonobservable) JavaScript values. This function is perfect to get copies of viewmodels.
  • ko.toJSON: This function uses the output from ko.toJS with JSON.stringify to produce a JSON string of the supplied object. This function accepts the same parameters as JSON.stringify.
主站蜘蛛池模板: 开平市| 隆安县| 南木林县| 巩留县| 桂阳县| 卢龙县| 西林县| 南安市| 临西县| 贵港市| 海伦市| 淮南市| 洛川县| 左权县| 台北县| 安溪县| 河池市| 丹巴县| 朝阳市| 修文县| 布尔津县| 闵行区| 通许县| 荆门市| 花垣县| 泸西县| 富民县| 洛南县| 荃湾区| 兴安盟| 广灵县| 铜山县| 临汾市| 黄陵县| 赣州市| 松阳县| 凌源市| 红原县| 铁力市| 大英县| 濉溪县|