2016-06-12 72 views
0

我有一个敲除计算器,其工作方式类似于检查/取消选中'yes'和'no'单选按钮的所有字符串。Knockout:扩展计算以处理不同的观察对象

我想尝试并扩展它,以便它可以针对不同的可观察属性。此刻,如果'optionList'中的所有'paramOne'属性都选择为'yes',那么我返回'是',类似于'否'。但我想使用它也用于'paramTwo'参数和'paramThree'参数等。

任何想法如何做到这一点?也许计算器在这里不是最合适的?

小提琴这里:https://jsfiddle.net/3kpx5qaf/

var AppViewModel = function() { 
    var self = this; 

    self.optionList = ko.observableArray(); 
    self.optionList().push({ 
    id : 'option1', 
    title: "option one", 
    paramOne:ko.observable(), 
    paramTwo: ko.observable(), 
    paramThree: ko.observable(), 
    paramFour: ko.observable() 
    }); 
    self.optionList().push({ 
    id : 'option2', 
    title: "option two", 
    paramOne:ko.observable(), 
    paramTwo: ko.observable(), 
    paramThree: ko.observable(), 
    paramFour: ko.observable() 
    }); 


    self.selectAll = ko.computed({ 
     read: function() { 

     var numNo = 0; 
     var numYes = 0; 
     var items = self.optionList(); 

     ko.utils.arrayFirst(items, function(item) { 
      if(item.paramOne() == "No"){ 
       numNo += 1; 
      }else if(item.paramOne() == "Yes"){ 
       numYes += 1; 
      } 
     }); 

     if(numNo == items.length){ 
      return "No"; 
     }else if(numYes == items.length){ 
      return "Yes"; 
     }else{ 
      return null; 
     } 
    }, 
    write: function(value) { 

     var items = self.optionList(); 

     ko.utils.arrayForEach(items, function(item) { 
      item.paramOne(value); 
     }); 
     } 
    }); 
} 

ko.applyBindings(new AppViewModel()); 
+0

“yes”和“no”代替“true”和“false”是什么意思?使用两个字符串来表示布尔值是麻烦的,容易出错的,具有国际化问题,并且所有这些都没有提供功能性好处。那为什么呢? – Tomalak

+0

同意,布尔值在这里会更适合,但基于knockout文档的示例基于单选按钮值(http://knockoutjs.com/documentation/checked-binding.html),所以这是我做的第一件事。 – user2424495

+0

我同意Jeroen关于XY问题。不要描述你想要实现的解决方案,而要描述你想解决的问题。 – Tomalak

回答

1

你很可能有一个XY-问题。你甚至可以通过问“X不是最合适的?”来暗示这一点。然而,你没有给出任何上下文或真实的例子(我希望你的真实代码没有“ParamOne”等变量?)。所以建议更好的解决方案很难。

你问你目前拥有的方法的问题,可以解释为:

我怎样才能干涸computed功能,使他们能够为paramTwo等被重用?

你可以简单地做通过提取那一点代码。这里有一个简单的例子只用JavaScript的:

var AppViewModel = function() { 
    var self = this; 

    // ...code omitted for brevity... 

    function getReadFn(propertyName) { 
    return function() { 

     var numNo = 0; 
     var numYes = 0; 
     var items = self.optionList(); 

     ko.utils.arrayFirst(items, function(item) { 
     if (item[propertyName]() == "No") { numNo += 1; } 
     else if (item[propertyName]() == "Yes") { numYes += 1; } 
     }); 

     if (numNo == items.length) { return "No"; } 
     else if (numYes == items.length) { return "Yes"; } 
     else { return null; } 
    }; 
    } 

    function getWriteFn(propertyName) { 
    return function(value) { 
     var items = self.optionList(); 

     ko.utils.arrayForEach(items, function(item) { 
      item[propertyName](value); 
     }); 
    }; 
    } 

    self.selectAll_paramOne = ko.computed({ 
    read: getReadFn("paramOne"), 
    write: getWriteFn("paramOne") 
    }); 

    self.selectAll_paramTwo = ko.computed({ 
    read: getReadFn("paramTwo"), 
    write: getWriteFn("paramTwo") 
    }); 

    //etc. 
} 

或者,你也可以create your own extender封装的逻辑。

但是,请再次仔细评估您的方法。具体如下:

  • 是“是”/“否”不是真正的本地化问题,您的支持不应该是布尔值吗?
  • 您的paramOne/etc可观察量实际上不是一个(可观察的)列表或简单的arry?
  • 难道你不能通过利用阵列功能,如mapsomeevery严重简化您的功能? (这取决于你需要支持/ polyfills你可以使用浏览器中位)
+0

同意布尔(请参阅我上面的评论)。另外,我给出了属性的基本名称(paramOne),仅仅因为我的真实世界的例子需要更多的解释 - 虽然这样更容易。观察对象可以是简单的属性而不是可观察对象,因为它们只受UI的影响。我需要支持IE8,尽管可能简化数组函数,但我还没有看过这个。 – user2424495

+0

在这种情况下,我支持我的答案:-) – Jeroen

+0

@ user2424495为了支持IE8,我建议包括Sugar.js库,它不仅填写缺少的数组方法,而且还增加了很多其他非常有用的实用程序。 – Tomalak

1

从你的问题我猜你想要变得"yes""no"如果一个选项的所有参数都同样"yes""no"属性 - 或如果存在不匹配,则为null

由于缺乏更好的名称,我们称该属性为overallSelection。由于它的价值取决于一系列可观察性,因此使用ko.computed来完成这项工作是很自然的。

您的示例应用程序由两部分组成 - 一个Option(其中包含几个参数)和一个OptionList(即选项的容器)。所以这就有三分之二的ViewModels,如下实施(通过点击下方的按钮运行的代码):

function Option(data) { 
 
    var self = this; 
 

 
    self.paramNames = ["paramOne", "paramTwo", "paramThree", "paramFour"]; 
 
    self.id = data.id; 
 
    self.title = data.title; 
 
    self.paramNames.forEach(function (paramName) { 
 
     self[paramName] = ko.observable(); 
 
    }); 
 

 
    self.overallSelection = ko.pureComputed(function() { 
 
     var ref; 
 
     self.paramNames.forEach(function (paramName, i) { 
 
      if (i === 0) ref = self[paramName](); 
 
      else if (ref !== self[paramName]()) ref = null; 
 
     }); 
 
     return ref; 
 
    }); 
 
} 
 
function OptionList(data) { 
 
    var self = this; 
 

 
    self.options = ko.observableArray(data.options.map(function (data) { 
 
     return new Option(data); 
 
    })); 
 
} 
 

 
// ------------------------------------------------------------------------- 
 
var vm = new OptionList({ 
 
    options: [ 
 
     {id: 'option1', title: 'option one'}, 
 
     {id: 'option2', title: 'option two'} 
 
    ] 
 
}); 
 
ko.applyBindings(vm);
ul { 
 
    list-style: none; 
 
    padding: 0; 
 
} 
 
.param { 
 
    display: inline-block; 
 
    width: 6em; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> 
 

 
<ul data-bind="foreach: options"> 
 
    <li> 
 
    <h4 data-bind="text: title"></h4> 
 
    <ul data-bind="foreach: paramNames"> 
 
     <li> 
 
     <span data-bind="text: $data" class="param"></span> 
 
     <label><input type="radio" value="yes" data-bind="checked: $parent[$data]"> Yes</label> 
 
     <label><input type="radio" value="no" data-bind="checked: $parent[$data]"> No</label> 
 
     </li> 
 
    </ul> 
 
    <b class="param">Overall</b> <span data-bind="text: overallSelection"></span> 
 
    </li> 
 
</ul>

注:

  • 目前这款采用"yes""no",但指示在评论中,我会不推荐这个变体。通过从value属性切换到checkedValue绑定(the docs on the checked binding的一部分),可以轻松更改为实际的布尔值。
  • 只要所有相关参数共享,我的overallSelection计算就会返回任何值。如果切换到布尔值,则不需要更改代码。
  • 整个事情的设计方式是将数据传递给viewmodel构造函数。以这种方式设计视图模型非常有用,它增加了它们的灵活性。
  • 这使用现代浏览器中可用的数组原型函数。为了支持新旧浏览器,请切换到ko.utils的等价物或使用polyfills(我倾向于推荐Sugar.js)。
  • 一般提示:命名很难。尽量不要给像selectAll这样的名称,而不要使用“全选”。