[javascript] 我是否過度使用Knockout映射插件總是用它來做我的viewmodel?


Answers

在使用Knockout一段時間後,我注意到映射插件有一些額外的選項,可以讓您對映射過程進行更精細的控制。

控制類型和生成的屬性數量

有幾種方法可以實現這一點,我將討論一些,但最終結果是你最終得到了映射插件的更輕的結果,因為一切都是不可觀察的。

基本上,你把你認為不會改變的一切都留作普通的屬性,只能從你想要觀察的特定項目中做出可觀察的。

使mapping省略某些屬性

您可以通過指定ignoreinclude類的內容,使映射插件完全從最終結果中省略屬性。 這兩者都以相反的方式完成了同樣的事情。

注意:樣本來自knockout.js映射插件文檔 ,我添加的評論

Mapping Plugin參數: 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);

映射插件參數: 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);

控制哪些屬性是可觀察的或不可觀察的

如果您需要包含屬性但是您認為它們不需要被觀察(無論出於何種原因),那麼映射插件可以提供幫助。

映射插件參數: 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(parent)的所有模型(子項),並且我希望通過訂閱完成此操作。

// 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%的可觀察對象。 深入了解映射配置選項並創建各種複雜和簡單的對象。 我們的想法是只獲得你需要的一切,或多或少。

Question

我還在學習Knockout的正確使用方法,我發現自己很快就擺脫了在設置我的viewmodel時輸入ko.observable而只是定義一個對象文字並通過映射插件傳遞它

var viewModel = ko.mapping.fromJS(data);

或者至少,將所有數據填充到viewModel上的屬性中,就像這樣

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

老實說,我這樣做的主要原因是為了避免ko.observableArray重複輸入ko.observableko.observableArray 。 我只想弄清楚這是否是一個好的方法,如果有任何缺點,一起刪除特定的var x = ko.observable()聲明。 此外,我正在加載所有這些,而不是響應任何ajax調用等,從我所知,這是映射插件的設計目的。

在使用knockout的工作中,你是否仍然手動逐個聲明observable,或者你使用了我使用的mapping.fromJS方法? 這樣經常使用映射插件有什麼特別的缺點嗎?

編輯:

具體例子

在這篇文章中 ,Steve通過這樣做來設置他的viewModel

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

通常,我也只是使用ko.mapping.fromJS來處理這種情況,特別是為了確保數組中的對像也被轉換為可觀察對象。 看看他做了什麼,我的做法似乎有些過度,並增加了一些不必要的開銷。




一個更簡單但更有幫助的附加組件可能是knockout-data-projections

目前,它不處理js到viewmodel映射,但它處理很好的視圖模型到JS映射。




Links