【问题标题】:Bindings doesn't work on nested template loaded by JSON in KnockoutJS绑定不适用于由 KnockoutJS 中的 JSON 加载的嵌套模板
【发布时间】:2015-08-29 01:29:29
【问题描述】:

昨天我提出了这个问题: How can I refresh or load JSON to my viewModel on Knockout JS with complex models

修复后一切正常,但是当我尝试使用复杂的 json 在 vi​​ewModel 中加载时,某些按钮(特别是在组上)不起作用。

恢复问题。我有一个带有先前序列化数据的 json。我使用该 json 填充 viewModel,这有效,正确加载数据但问题出在“组”模板中,因为数据已加载但按钮不起作用,唯一有效的按钮是“删除团体”。 (请参考图片)

有办法解决这个问题吗?谢谢。

有问题的 Jsfiddle 示例 http://jsfiddle.net/y98dvy56/26/

!Check this picture. The red circles indicates the buttons with problems. The green circles indicates the buttons without problems.

这是正文 html

  <div class="container">
    <h1>Knockout.js Query Builder</h1>
    <div class="alert alert-info">
      <strong>Example Output</strong><br/>

    </div>
    <div data-bind="with: group">
      <div data-bind="template: templateName"></div>
    </div>
    <input type="submit" value="Save" data-bind="click: Save"/>
  </div>

  <!-- HTML Template For Conditions -->
  <script id="condition-template" type="text/html">
    <div class="condition">
      <select data-bind="options: fields, value: selectedField"></select>
      <select data-bind="options: comparisons, value: selectedComparison"></select>
      <input type="text" data-bind="value: value"></input>
      <button class="btn btn-danger btn-xs" data-bind="click: $parent.removeChild"><span class="glyphicon glyphicon-minus-sign"></span></button>
    </div>
  </script>

  <!-- HTML Template For Groups -->
  <script id="group-template" type="text/html">
    <div class="alert alert-warning alert-group">
      <select data-bind="options: logicalOperators, value: selectedLogicalOperator"></select>
      <button class="btn btn-xs btn-success" data-bind="click: addCondition"><span class="glyphicon glyphicon-plus-sign"></span> Add Condition</button>
      <button class="btn btn-xs btn-success" data-bind="click: .addGroup"><span class="glyphicon glyphicon-plus-sign"></span> Add Group</button>
      <button class="btn btn-xs btn-danger" data-bind="click: $parent.removeChild"><span class="glyphicon glyphicon-minus-sign"></span> Remove Group</button>
      <div class="group-conditions">
        <div data-bind="foreach: children">
          <div data-bind="template: templateName"></div>
        </div>
      </div>
    </div>
  </script>

  <!-- js -->
  <script src="js/vendor/knockout-2.2.1.js"></script>
  <script src="js/vendor/knockout-mapping.js"></script>
  <script src="js/condition.js"></script>
  <script src="js/group.js"></script>
  <script src="js/viewModel.js"></script>
  <script>
  window.addEventListener('load', function(){
  var json = 
{"group":{"templateName":"group-template","children":[{"templateName":"condition-template","fields":["Points","Goals","Assists","Shots","Shot%","PPG","SHG","Penalty Mins"],"selectedField":"Points","comparisons":["=","<>","<","<=",">",">="],"selectedComparison":"=","value":0,"text":"Points = 0"},{"templateName":"condition-template","fields":["Points","Goals","Assists","Shots","Shot%","PPG","SHG","Penalty Mins"],"selectedField":"Points","comparisons":["=","<>","<","<=",">",">="],"selectedComparison":"=","value":0,"text":"Points = 0"},{"templateName":"condition-template","fields":["Points","Goals","Assists","Shots","Shot%","PPG","SHG","Penalty Mins"],"selectedField":"Points","comparisons":["=","<>","<","<=",">",">="],"selectedComparison":"=","value":0,"text":"Points = 0"},{"templateName":"group-template","children":[{"templateName":"condition-template","fields":["Points","Goals","Assists","Shots","Shot%","PPG","SHG","Penalty Mins"],"selectedField":"Points","comparisons":["=","<>","<","<=",">",">="],"selectedComparison":"=","value":0,"text":"Points = 0"},{"templateName":"condition-template","fields":["Points","Goals","Assists","Shots","Shot%","PPG","SHG","Penalty Mins"],"selectedField":"Points","comparisons":["=","<>","<","<=",">",">="],"selectedComparison":"=","value":0,"text":"Points = 0"},{"templateName":"condition-template","fields":["Points","Goals","Assists","Shots","Shot%","PPG","SHG","Penalty Mins"],"selectedField":"Points","comparisons":["=","<>","<","<=",">",">="],"selectedComparison":"=","value":0,"text":"Points = 0"}],"logicalOperators":["AND","OR"],"selectedLogicalOperator":"AND","text":"(Points = 0 AND Points = 0 AND Points = 0)"}],"logicalOperators":["AND","OR"],"selectedLogicalOperator":"AND","text":"(Points = 0 AND Points = 0 AND Points = 0 AND (Points = 0 AND Points = 0 AND Points = 0))"},"text":"(Points = 0 AND Points = 0 AND Points = 0 AND (Points = 0 AND Points = 0 AND Points = 0))"};

    var vm = new QueryBuilder.ViewModel();
    ko.mapping.fromJS(json.group, {}, vm.group);
    ko.applyBindings(vm);   

  }, true);
  </script>

Condition.js:

window.QueryBuilder = (function(exports, ko){

  function Condition(){
    var self = this;

    self.templateName = 'condition-template';

    self.fields = ko.observableArray(['Points', 'Goals', 'Assists', 'Shots', 'Shot%', 'PPG', 'SHG', 'Penalty Mins']);
    self.selectedField = ko.observable('Points');

    self.comparisons = ko.observableArray(['=', '<>', '<', '<=', '>', '>=']);

    self.selectedComparison = ko.observable('=');

    self.value = ko.observable(0);
  }

  exports.Condition = Condition;
  return exports;

})(window.QueryBuilder || {}, window.ko);

Group.js

window.QueryBuilder = (function(exports, ko){

  var Condition = exports.Condition;

  function Group(){
    var self = this;

    self.templateName = 'group-template';
    self.children = ko.observableArray();
    self.logicalOperators = ko.observableArray(['AND', 'OR']);
    self.selectedLogicalOperator = ko.observable('AND');

    // give the group a single default condition
    self.children.push(new Condition());

    self.addCondition = function(){
        self.children.push(new Condition());
    };

    self.addGroup = function(){
        self.children.push(new Group());
    };

    self.removeChild = function(child){
        self.children.remove(child);
    };
  }

  exports.Group = Group;
  return exports;

})(window.QueryBuilder || {}, window.ko);

ViewModel.js

window.QueryBuilder = (function(exports, ko){

  var Group = exports.Group;

  function ViewModel() {
    var self = this;
    self.group = ko.observable(new Group());

    self.load = function (data) {
        ko.mapping.fromJS(data, self);
    }   

    self.Save = function () {
        console.log(ko.toJSON(self));
    }   
  }

  exports.ViewModel = ViewModel;
  return exports;

})(window.QueryBuilder || {}, window.ko);

【问题讨论】:

  • 你能用你的代码提供一个 jsfiddle 吗?
  • @omerio 是的,这是 jsfiddle 代码。 jsfiddle.net/y98dvy56/26
  • 您的问题是由于映射插件使您的数据可观察,但没有添加添加、删除等功能引起的。如果您在将 json 数据插入视图模型时对其进行控制台日志记录,您会注意到数据是可观察的,但功能缺失。您需要提供一个映射来自定义您的组、条件等。构造函数,如此处所述knockoutjs.com/documentation/plugins-mapping.html
  • @omerio 你能根据我的代码给我一个简单的例子吗?这对我有很大帮助。
  • 我想我已经解决了,现在就更新答案

标签: javascript json mvvm knockout.js


【解决方案1】:

您的问题是由于映射插件使您的数据可观察,但不会使用模型中的函数(例如添加、删除等...函数)来扩充您的数据。如果您在将 json 数据插入视图模型时对其进行控制台日志记录,您会注意到数据是可观察的,但功能缺失。您需要提供一个映射来自定义您的 Group、Condition 等构造函数。因为在您的情况下,children 数组是混合类型(条件或组)这是一个自定义映射来处理这个问题:

var childrenMapping = {
    'children': {
        create: function(options) {
            var data = options.data;
            console.log(data);
            var object;
            switch(data.templateName) {
                case 'condition-template':
                    object = new QueryBuilder.Condition(data);
                    break;
                case 'group-template':
                    object = new QueryBuilder.Group(data);
                    break;
            }
            return object;
        }
    }
};      

那么你只需要在你的初始映射中提供这个映射

ko.mapping.fromJS(json.group, childrenMapping, vm.group);

然后在Group对象的构造函数里面:

 function Group(data){

   var self = this;

   self.templateName = 'group-template';
   ...

   ko.mapping.fromJS(data, childrenMapping, this);    
}

您还需要更新 Condition 构造函数以接受映射提供的数据,但由于条件没有子项,因此您无需在此处提供 childrenMapping:

 function Condition(data){

     var self = this;

     self.templateName = 'condition-template';

     ...

     ko.mapping.fromJS(data, {}, this);    
 }

我在两个函数的末尾都有映射,因此映射的值会覆盖您的初始值。

这里更新的jsfiddle:

http://jsfiddle.net/omerio/y98dvy56/32/

这个答案是相关的: knockout recursive mapping issue

【讨论】:

  • 感谢您的宝贵时间。我对映射非常了解,唯一的区别(我的代码中缺少)是按模板名称切换。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-23
  • 2013-07-19
  • 2019-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多