ko.toJSON 似乎忽略字段
我正在使用 knockoutjs 编写动态查询编辑器,但 ko.toJSON 不会输出字段,直到它们被数据绑定表单元素修改为止。请参阅 this jsfiddle 与以下视图:
<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);
尽管视图模型显然已预先初始化了字段(例如 join 属性),除非用户在 UI 中更改它们,否则它们不会显示在 JSON 对象中。
有人可以解释一下我做错了什么以及如何解决它吗?这实际上看起来像是 knockoutjs 本身的一个错误。如果归根结底,如果值不存在,我将在构建查询时使用默认值,但这似乎是一个蹩脚的解决方案
I am coding a dynamic query editor with knockoutjs, but ko.toJSON is not outputting the fields until they have been modified by a data-bound form element. See this jsfiddle with the following view:
<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>
And this code:
// 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);
Though the view model clearly has fields pre-initialized (such as the join property), they're not showing up in the JSON object until the user changes them in the UI.
Can someone please explain what I'm doing wrong and how to fix it? This actually seems like a bug with knockoutjs itself. If it comes down to it, I'll just use defaults when building the query if the values aren't there, but this seems like a crappy solution
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您的代码中有一个微妙的问题,导致许多人抓狂。当您在 select 元素上使用
options
绑定和value
绑定时,需要在value
之前指定options
。options
绑定构建可用选项,然后值绑定强制所选选项和视图模型值同步。如果它们的顺序错误,则value
绑定首先运行,并发现没有可供选择的匹配选项,因此它将值设置为 null。这是您切换顺序的小提琴: http://jsfiddle.net/rniemeyer/32fYk/
这已登录 github几次,最近一次是在这里:https://github.com/SteveSanderson/knockout/issues/58。目前没有一种简单的方法可以强制同一数据绑定中的绑定在另一个绑定之前运行。希望这个问题在某个时候能够得到解决。我可以想到一种修复方法,通过让两个绑定检查另一个绑定是否列出来专门处理这种情况。
There is a subtle issue in your code that has caused many people to tear their hair out. When you use the
options
binding with thevalue
binding on a select element, you need to specifyoptions
beforevalue
.The
options
binding builds the available options and then the value binding enforces that the selected option and view model value are in sync. If you have them in the wrong order, then thevalue
binding runs first and sees that there is not a matching option to select, so it sets the value to null.Here is your fiddle with the order switched: http://jsfiddle.net/rniemeyer/32fYk/
This has been logged on github a couple of times, most recently here: https://github.com/SteveSanderson/knockout/issues/58. There is currently not a simple way to enforce that a binding runs before another one in the same data-bind. Hopefully, at some point this will be addressed. I can think of a fix that would specifically handle this case by having the two bindings check if the other one is listed.