Knockout:扩展计算以处理不同的可观察量

时间:2016-06-12 10:27:06

标签: knockout.js

我有一个淘汰赛计算方法类似于检查/取消选中所有但是在字符串上为“是”'并且没有'单选按钮。

我想尝试扩展它,以便它可以对付不同的可观察属性。目前,如果所有' paramOne'来自' optionList'的属性被选为'是'然后我回答“是”',类似于' no'。但是,我也想将它用于“paramTwo”#39;参数和' 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());

2 个答案:

答案 0 :(得分: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观察者实际上不应该是(可观察的)列表还是普通的?
  • 您是否可以通过使用mapsomeevery等数组函数来大幅简化您的功能? (这取决于您需要支持/可以使用的polyfill的浏览器)

答案 1 :(得分:1)

如果选项的所有参数均为"yes""no" - 或{{1},我会根据您的问题收集您想要的属性"yes""no"如果有不匹配的话。

如果缺少更好的名字,请拨打该属性null。由于它的价值取决于一堆可观测量,因此使用ko.computed来完成这项任务是很自然的。

您的示例应用程序由两部分组成 - overallSelection,其中包含多个参数,以及Option,即选项的容器。这是两个视图模型,实现如下(通过单击下面的按钮运行代码):



OptionList

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绑定(the docs on the checked binding的一部分),可以轻松更改为实际布尔值。
  • 我的checkedValue computed会返回任何值,只要所有相关参数共享它。如果切换到布尔值,则无需更改代码。
  • 整个事情的设计方式是将数据传递给viewmodel构造函数。以这种方式设计视图模型很有用,它增加了它们的灵活性。
  • 这使用了现代浏览器中可用的数组原型函数。要支持新旧浏览器,请切换到overallSelection的等效文件或使用polyfills(我倾向于推荐Sugar.js)。
  • 一般提示:命名很难。尽量不要将ko.utils之类的名称提供给那些没有的名称,实际上,&#34;选择所有&#34;。