javascript - 绑定(bind)不适用于 KnockoutJS 中 JSON 加载的嵌套模板

标签 javascript json mvvm knockout.js

昨天我提出了这个问题: 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>

条件.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);

最佳答案

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

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

关于javascript - 绑定(bind)不适用于 KnockoutJS 中 JSON 加载的嵌套模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30813208/

相关文章:

javascript - Angular $scope.$watch newVal !== oldVal

c# - 如何获取通过Json获取的对象内部的值

json - Spark + Json4s 序列化问题

wpf - 数据触发器未触发

c# - 将 App.xaml 设置为 UserControl WPF(以连接定位器)

javascript - PHP 和 JavaScript 的正则表达式

javascript - JavaScript 中的 NodeList 对象

javascript - 为什么这个 RegExp exec 会导致无限循环?

android - @JsonCreator 的选择性使用

c# - WPF 绑定(bind)不更新 View