我是否过度使用Knockout映射插件总是用它来做我的viewmodel?

时间:2011-09-20 16:03:22

标签: javascript mvvm knockout.js knockout-mapping-plugin

我还在学习Knockout的正确使用方法,我发现自己在设置我的viewmodel时很快就忘记了输入ko.observable而只是定义了一个对象文字并通过映射插件传递了它

之类的东西
var viewModel = ko.mapping.fromJS(data);

或者至少是将我的所有数据填充到viewModel上的属性中的内容,如此

var viewModel = { 
    ... events etc ... , 
    "data": ko.mapping.fromJS(data)
}

说实话,我一直这样做的主要原因是必须重复输入ko.observableko.observableArray。我只是想弄清楚这是否是一个好的方法,如果有任何缺点,一起放弃特定的var x = ko.observable()声明。另外,我正在加载所有这些,而不是响应任何ajax调用等,从我可以看出,这是映射插件的设计目的。

在使用淘汰赛的工作中,你是否仍然手动逐个声明可观察量,或者你使用了我使用的mapping.fromJS方法?像这样经常使用映射插件有什么特别的缺点吗?

编辑:

具体例子

In this article,Steve通过

设置了他的viewModel
var initialData = [ { ... } , { ... } ]; // json from the serializer
var viewModel = {
    gifts : ko.observableArray(initialData)
};

通常情况下,我也只是使用ko.mapping.fromJS来解决这种情况,特别是要确保数组中的对象也被转换为可观察对象。看看他做了什么,我的做法似乎有些过度,并增加了一些不必要的开销。

4 个答案:

答案 0 :(得分:58)

使用Knockout一段时间后,我注意到映射插件有一些额外的选项,可以让你对映射过程进行更精细的控制。

控制类型和生成的属性数量

有几种方法可以实现这一点,我将讨论一些,但最终结果是你最终得到了映射插件的更轻的结果,因为一切都是不可观察的。

基本上,你把你认为不会改变的一切都留作普通的财产,只能从你想要观察的特定项目中做出可观察的。

使mapping省略某些属性

您可以通过指定ignoreinclude之类的内容,使映射插件完全从最终结果中省略属性。这两者都以相反的方式完成了同样的事情。

注意:样本来自knockout.js mapping plugin documentation,我添加的评论

Mapping Plugin Argument:include

以下代码段将省略源对象中的所有属性,而不是通过include参数传递的属性。

// specify the specific properties to include as observables in the end result 
var mapping = {
    // only include these two properties
    'include': ["propertyToInclude", "alsoIncludeThis"]
}

// viewModel will now only contain the two properties listed above, 
//    and they will be observable
var viewModel = ko.mapping.fromJS(data, mapping);

Mapping Plugin Argument:ignore

如果您只想从源对象中省略某些属性,请使用ignore参数,如下所示。除了指定的属性外,它将从源对象中的所有属性创建可观察对象。

// specify the specific properties to omit from the result, 
//    all others will be made observable
var mapping = {
    // only ignore these two properties
    'ignore': ["propertyToIgnore", "alsoIgnoreThis"]
}

// viewModel will now omit the two properties listed above, 
//    everything else will be included and they will be an observable
var viewModel = ko.mapping.fromJS(data, mapping);

控制哪些属性可观察或不可观察

如果你需要包含属性,但你认为它们不需要被观察(无论出于何种原因),那么映射插件可以提供帮助。

Mapping Plugin Argument:copy

如果您希望映射插件只是简单地复制普通属性而不使它们可观察,请使用此参数,如下所示。

// tell the mapping plugin to handle all other properties normally, 
//    but to simply copy this property instead of making it observable
var mapping = {
    'copy': ["propertyToCopy"]
}
var viewModel = ko.mapping.fromJS(data, mapping);

完全控制映射过程

如果您希望100%控制在映射过程中创建的内容,包括在对象中放置闭包和订阅的能力,那么您希望使用“创建”选项。

具有计算属性的简单结果

以下是我将数据从ajax调用映射到具有results属性的对象的示例。我不想要任何可观察的东西,我只想要一个简单的生成属性,它将由对象上的其他简单属性组成。也许不是最引人注目的例子,但它展示了功能。

var searchMappingConfig = {
    // specific configuration for mapping the results property
    "results": {
                    // specific function to use to create the items in the results array
        "create": function (options) {
            // return a new function so we can have the proper scope/value for "this", below
            return new function () {

                // instead of mapping like we normally would: ko.mapping.fromJS(options.data, {}, this);
                // map via extend, this will just copy the properties from the returned json element to "this"
                // we'll do this for a more light weight vm since every last property will just be a plain old property instead of observable
                $.extend(this, options.data);

                // all this to add a vehicle title to each item
                this.vehicleTitle = this.Year + "<br />" + this.Make + " " + this.Model;
                }, this);
            };
        }
    }
}

订阅和关闭和映射,哦我的

另一种情况是,如果您想在结果中使用闭包和订阅。此示例太长,无法完整地包含在内,但它适用于车辆品牌/模型层次结构。如果模型未启用,我希望取消启用给定make(父级)的所有模型(子级),并且我希望通过订阅完成此操作。

// here we are specifying the way that items in the make array are created, 
//    since makes has a child array (Models), we will specify the way that 
//    items are created for that as well
var makesModelsMappingConfig = {
   // function that has the configuration for creating makes
   "create": function (options) {
      // return a new function so we can have the proper 
      //    scope/value for "this", below
      return new function () {

         // Note: we have a parent / child relationship here, makes have models. In the 
         //    UI we are selecting makes and then using that to allow the user to select 
         //    models. Because of this, there is going to be some special logic in here 
         //    so that all the child models under a given make, will automatically 
         //    unselect if the user unselects the parent make.

         // make the selected property a private variable so it can be closure'd over
         var makeIsSelected = ko.protectedComputed(false);

         // expose our property so we can bind in the UI
         this.isSelected = makeIsSelected;

         // ... misc other properties and events ...

         // now that we've described/configured how to create the makes, 
         //    describe/configure how to create the models under the makes
         ko.mapping.fromJS(options.data, {
            // specific configuration for the "Models" property                  
            "Models": {
               // function that has the configuration for creating items 
               //    under the Models property
               "create": function (model) {

                  // we'll create the isSelected as a local variable so 
                  //    that we can flip it in the subscription below, 
                  //    otherwise we wouldnt have access to flip it
                  var isSelected = ko.protectedComputed(false);

                  // subscribe to the parents "IsSelected" property so 
                  //    the models can select/unselect themselves
                  parentIsSelected.current.subscribe(function (value) {
                     // set the protected computed to the same 
                     //    value as its parent, note that this 
                     //    is just protected, not the actual value
                     isSelected(value);
                  });


                  // this object literal is what makes up each item 
                  //    in the Models observable array 
                  return {
                     // here we're returning our local variable so 
                     //    we can easily modify it in our subscription
                     "isSelected": isSelected,

                     // ... misc properties to expose 
                     //     under the item in the Model array ...

                  };
               }
            }
         }, this);
      };
   }
};

总而言之,我发现你很少需要100%的对象传递给插件,而你很少需要100%的对象可观察。深入了解映射配置选项并创建各种复杂和简单的对象。我们的想法是只获得你需要的一切,或多或少。

答案 1 :(得分:22)

我向你提出的建议也是我在https://stackoverflow.com/questions/7499133/mapping-deeply-hierarchical-objects-to-custom-classes-using-knockout-mapping-plug.

回答的另一个问题

您使用映射插件的原因是合理的,也是我使用的插件。为什么要输入比你更多的代码呢?

根据我的淘汰经验(全部4个月),我发现我手动做的越少,让淘汰程序做他们的事情,我的应用程序似乎运行得越好。我的建议是先尝试最简单的方法。如果它不能满足您的需求,请查看简单方法如何做“事情”并确定必须更改哪些以满足您的需求。

答案 2 :(得分:4)

艾伦,我最近在Knockout.js的学习经历与你的相似。我们使用来自服务器的深层次对象图,我已经定义了显式的可实例化视图模型函数,它们保留了它的基本结构。

我首先将每个属性显式定义为相关视图模型上的可观察对象,但很快就失控了。此外,切换到使用映射插件的一个主要原因是我们必须经常将图表的Ajax帖子发送回服务器,并将其与持久版本合并,然后在服务器上验证,以便众多属性可以修改更改和集合,并将新实例作为Ajax结果返回,必须将其与客户端表示重新合并。这变得非常困难,映射插件通过允许指定用于解析添加/删除/更新的标识符以及将更新的图形重新映射到原始图形上来帮助大量时间。

通过使用子视图模型的“创建”选项,它还有助于原始图形创建。在每个视图模型构造函数中,我接收对父视图模型的引用以及用于构造子视图模型的数据,然后创建进一步的映射选项以从传入的子数据创建孙子。

我最近发现的唯一(轻微)缺点,如this question中详细说明的那样,当执行ko.mapping.toJSON时,它不会挂钩到您在视图原型上定义的任何toJSON覆盖模型,以从序列化中排除属性。我已经能够通过在取消映射中指定忽略选项来解决这个问题,正如Ryan Niemeyer在该帖子中所建议的那样。

总而言之,我肯定会坚持使用映射插件。 Knockout.js规则。

答案 3 :(得分:1)

更简单但有帮助的附加组件可能是knockout-data-projections

目前,它不处理js到viewmodel映射,但它处理了很好的视图模型到JS映射。