2011-09-08 156 views
4

我正在编写一个带有knockoutjs的动态查询编辑器,但是ko.toJSON不会输出字段,直到它们被数据绑定的表单元素修改为止。见this jsfiddle有以下几种观点:ko.toJSON似乎忽略字段

<span data-bind="template: {name: 'filterGroupTemplate', data: viewModel}"></span> 

<script type="text/x-jquery-tmpl" id="filterGroupTemplate"> 
    <div class="filterGroup"> 
     <div class="filterGroupParams"> 
      Match 
      <select name="join" data-bind="value: join, options: joins"></select> 
      of the following rules: 
      <button class="addFilter" data-bind="click: addFilter">+</button> 
      <button class="addGroup" data-bind="click: addGroup">{...}</button> 
      <button class="removeGroup">x</button> 
     </div> 
     <span data-bind='template: {name: "filterTemplate", foreach: filters }'></span> 
    </div> 
</script> 

<script type="text/x-jquery-tmpl" id="filterTemplate"> 
    <div class="filter"> 
     {{if $data.filters }}  
      <div data-bind='template: "filterGroupTemplate"'> 
      </div>     
     {{else}} 
      <select data-bind="value: field, options: fields"></select>  
      <select data-bind="value: modifier, options: modifiers"></select> 
      <input type="text" data-bind="value: criteria" /> 
      <button>x</button> 
     {{/if}} 
    </div> 
</script> 

<h2>ViewModel JSON</h2> 
<div data-bind="text: dev()"></div> 

而这种代码:

// filter class 
var Filter = function() { 
    this.field = ko.observable(); 
    this.modifier = ko.observable(); 
    this.criteria = ko.observable(); 
}; 

// filter group class 
var FilterGroup = function() { 
    // Include a blank filter in every group 
    this.join = ko.observable('All'); 
    this.filters = ko.observableArray([new Filter()]); 
    this.addFilter = function() { 
     var filter = new Filter(); 
     this.filters().push(filter); 
     this.filters.valueHasMutated(); 
    }; 
    this.addGroup = function() { 
     var group = new FilterGroup(); 
     this.filters().push(group); 
     this.filters.valueHasMutated(); 
    }; 
}; 

// Data 
var joins = ['All', 'Any']; 
var modifiers = [ 
    'equals', 
    'not equal to', 
    'less than', 
    'greater than', 
    'contains', 
    'does not contain', 
    'starts with' 
]; 
var fields = ['f1','f2','f3']; 

var viewModel = new FilterGroup(); 
function dev(){ 
    return ko.toJSON(viewModel); 
} 

ko.applyBindings(viewModel); 

尽管视图模型显然有场预先初始化(如联接属性),他们没有显示了直到用户在UI中更改它们为止的JSON对象。

有人可以请解释我做错了什么,以及如何解决它?这实际上就像是一个knockoutjs本身的bug。如果涉及到它,如果值不存在,我将在构建查询时使用默认值,但这似乎是一个蹩脚的解决方案

回答

8

您的代码中存在一个微妙的问题,导致许多人撕掉他们的头发。当您在select元素上使用options绑定与value绑定时,您需要在value之前指定options

options绑定会构建可用选项,然后值绑定会强制所选选项和视图模型值同步。如果按错误顺序排列它们,则value绑定首先运行,并且看到没有匹配的选项可供选择,因此它将该值设置为null。

这里是秩序的提琴切换:http://jsfiddle.net/rniemeyer/32fYk/

这已被记录在GitHub上了几次,最近一次是在这里:https://github.com/SteveSanderson/knockout/issues/58。目前还没有一种简单的方法来强制绑定在同一个数据绑定中的另一个绑定之前运行。希望在某些时候这将得到解决。我可以通过让两个绑定检查是否列出另一个绑定来解决这个问题。

+1

非常感谢!这工作很好。我唯一的问题是,这个问题似乎仍然适用于输入元素上的约束数据绑定,但这很重要,因为不应该有空白的输入值。现在添加删除项目的功能... 为了记录,您的博客是我的谷歌搜索的主要来源 - 我花了几个小时阅读几乎所有的东西。 – daedalus28

+4

当序列化为JSON时,值为null的属性将被忽略,因此您可以通过将'criteria'强制为Filter的构造函数中的空字符串来更正您的输入元素('this.criteria = ko.observable('') ;') –

+1

谢谢,那也工作得很好:) – daedalus28