我有一个使用knockout实现的n级复选框树,其中父级chechbox的选择应该选择其子级(但不是相反)并且一旦提交数据,所选元素的id应转换为JSON被发送到服务器。
我无法弄清楚如何单向复选框关系,也无法弄清楚如何以这样的方式过滤我的最终json:
如果选择了父级(因为它也意味着所有子级都被选中),则不在json中发送其子ID
如果只选择了一个或几个孩子,如何不发送父ID。
列表项的模型是:
marketingListsItem = function (data, parent) {
var self = this;
self.Name = ko.observable(data.Name);
self.Selected = ko.observable(data.Selected);
self.Parent = ko.observable(parent);
self.Children = ko.observableArray([]);
self.Id = ko.observable(data.Id);
self.DateAdded = ko.observable(new Date(data.DateAdded));
self.DateModified = ko.observable(data.DateModified);
if (data.Children) {
ko.utils.arrayForEach(data.Children, function (child) {
self.Children.push(new marketingListsItem(child, this));
}.bind(this));
};
}
以下是视图模型部分:
marketingListsViewModel = {
marketingLists: mapping.fromJS([]),
originatorConnectionName: ko.observable(''),
selectedMarketingListIds: ko.observableArray([])
},
init = function (connectionId, connectionName) {
marketingListsViewModel.originatorConnectionName(connectionName);
marketingListsViewModel.getFieldMapping = function () {
require(['mods/fieldmapping'], function (fieldmapping) {
fieldmapping.init(connectionId, connectionName);
});
};
// Here I only managed to filter the parent level selections
marketingListsViewModel.selectedLists = ko.computed(function () {
return ko.utils.arrayFilter(marketingListsViewModel.marketingLists(), function (item) {
return item.Selected() == true;
});
});
marketingListsViewModel.saveMarketingListChanges = function () {
// Which I can filter my JSON to include them but the children are missing
var latestMarketingListChanges = ko.toJSON(marketingListsViewModel.selectedLists, ["Id"]);
console.log(latestMarketingListChanges);
amplify.request("updateExistingMarketingLists", { cid: connectionId, ResponseEntity: { "id": connectionId, "selectedMarketListIds": latestMarketingListChanges } },
function (data) {
console.log(data);
});
}
amplify.request("getExistingMarketingLists", { cid: connectionId }, function (data) {
showMarketingLists();
mapping.fromJS(data.ResponseEntity, dataMappingOptions, marketingListsViewModel.marketingLists);
ko.applyBindings(marketingListsViewModel, $('#marketingLists')[0]);
});
};
最后这里是观点:
<div id="marketingListsContainer">
<ul data-bind="template: {name: 'itemTmpl' , foreach: marketingLists}"></ul>
<script id="itemTmpl" type="text/html">
<li>
<label><input type="checkbox" data-bind="checked: Selected" /><span data-bind='text: Name'></span></label>
<ul data-bind="template: { name: 'itemTmpl', foreach: Children }" class="childList"></ul>
</script>
</div>
<a class="s_button modalClose right" href="#"><span data-bind="click: saveMarketingListChanges">Save and close</span></a><br>
答案 0 :(得分:2)
感谢您的回答。问题是,如果已经通过服务器中的数据检查了Parent,那么现在使用您的解决方案,那么它的子项检查值不会发生变异。
我已将以下内容添加到模型中以解决此问题:
self.sync = ko.computed(function () {
return self.Selected.valueHasMutated();
});
对于那些稍后会发现这篇文章的人,可能想知道最终结果是什么:
define('mods/marketinglists', ["knockout", "libs/knockout.mapping", "libs/knockout.validation", "datacontext", "mods/campaigner", "text!templates/marketinglists.html", "text!styles/marketinglists.css"],
function (ko, mapping, validation, datacontext, campaigner, html, css) {
'use strict';
var
marketingListsItem = function (data, parent) {
var self = this;
self.Name = ko.observable(data.Name);
self.Selected = ko.observable(data.Selected);
self.Parent = ko.observable(parent);
self.Children = ko.observableArray([]);
self.Id = ko.observable(data.Id);
self.DateAdded = ko.observable(new Date(data.DateAdded));
self.DateModified = ko.observable(data.DateModified);
// If node contains children define each one as a marketingListItem in itself
// and bind it to the the model
if (data.Children) {
ko.utils.arrayForEach(data.Children, function (child) {
self.Children.push(new marketingListsItem(child, this));
}.bind(this));
};
// Watch for value changes in parent and check children
// if the parent was checked
self.Selected.subscribe(function (newValue) {
if (newValue === true) {
for (var i = 0; i < self.Children().length; i++) {
self.Children()[i].Selected(true);
}
}
else {
if (self.Parent() != null) { self.Parent().Selected(false); }
}
});
// Make sure subscribers have been notified when needed
self.sync = ko.computed(function () {
return self.Selected.valueHasMutated();
});
},
dataMappingOptions = {
key: function (data) {
return data.Id;
},
create: function (options) {
return new marketingListsItem(options.data, null);
}
},
showMarketingLists = function () {
campaigner.addStylesToHead(css);
campaigner.addModalWindow(html, {
windowSource: "inline",
width: 700,
height: '340'
});
},
marketingListsViewModel = {},
init = function (connectionId, connectionName) {
// Define marketingLists as an observable array from JS object
marketingListsViewModel.marketingLists = mapping.fromJS([]);
marketingListsViewModel.originatorConnectionName = ko.observable('');
// Set the name for the marketing list
marketingListsViewModel.originatorConnectionName(connectionName);
marketingListsViewModel.getFieldMapping = function () {
require(['mods/fieldmapping'], function (fieldmapping) {
fieldmapping.init(connectionId, connectionName);
});
};
marketingListsViewModel.selectedLists = ko.computed(function () {
var selectedItems = [];
ko.utils.arrayFilter(
marketingListsViewModel.marketingLists(),
function (item) {
// If a parent a selected its being collected
if (item.Selected() == true) selectedItems.push(item);
else {
// If a child is slected it is collected
ko.utils.arrayForEach(item.Children(), function (child) {
if (child.Selected()) selectedItems.push(child);
else {
ko.utils.arrayForEach(child.Children(),
// Finally if children's child is selected its collected
function (childChildren) {
if (childChildren.Selected())
selectedItems.push(childChildren);
});
}
})
}
});
return selectedItems;
});
marketingListsViewModel.saveMarketingListChanges = function () {
// Pick only the selected elements and parse only the Id
var latestMarketingListChanges = ko.toJSON
(marketingListsViewModel.selectedLists,
["Id"]);
console.log(latestMarketingListChanges);
// Send the latest marketing lists changes Ids to the server
amplify.request("updateExistingMarketingLists",
{
cid: connectionId,
ResponseEntity:
{
"id": connectionId,
"selectedMarketListIds": latestMarketingListChanges
}
},
function (data) {
console.log(data);
});
}
amplify.request("getExistingMarketingLists", { cid: connectionId },
function (data) {
showMarketingLists();
mapping.fromJS(
data.ResponseEntity,
dataMappingOptions,
marketingListsViewModel.marketingLists);
ko.applyBindings(marketingListsViewModel, $('#marketingLists')[0]);
});
};
return {
init: init,
marketingListsViewModel: marketingListsViewModel,
html: html,
css: css
}
});
使用此视图:
<div id="marketingListsContainer">
<ul data-bind="template: {name: 'itemTmpl' , foreach: marketingLists}"></ul>
<script id="itemTmpl" type="text/html">
<li>
<!-- ko if: $data.Parent -->
(my parent is: <span data-bind="text: $data.Parent().Name"></span>)
<!-- /ko -->
<label><input type="checkbox" data-bind="checked: Selected" /><span data-bind='text: Name'></span></label>
<ul data-bind="template: { name: 'itemTmpl', foreach: Children }" class="childList"></ul>
</script>
</div>
<a class="s_button modalClose right" href="#"><span data-bind="click: saveMarketingListChanges">Save and close</span></a><br>
答案 1 :(得分:1)
您可以向所选绑定添加订阅事件,并执行类似这样的操作,以便在检查父级时将所有子项标记为已选中。
self.Selected.subscribe( function ( newValue ) {
if ( newValue === true ) {
for ( i=0; i<self.Children().length; i++) {
self.Children()[i].Selected(true);
}
}
else {
if ( self.Parent() != null ) {
self.Parent().Selected(false);
}
}
} ) ;
然后在您的保存方法中,您将需要对列表进行一些自定义遍历逻辑
marketingListsViewModel.saveMarketingListChanges = function (
var allSelected = ko.toJS( marketingListsViewModel.selectedLists );
var ids = [];
for ( i = 0; i < allSelected.length; i++ ) {
ids.concat ( marketingListsViewModel.getIds( allSelected[i] ) );
}
var latestMarketingListChanges = ko.toJSON(ids);
...
);
marketingListsViewModel.getIds = function( item ) {
var ids = []
if ( item.Selected === true ) {
ids.add( item.Id );
}
else if ( item.Children.length > 0 ) {
for ( i = 0; i < item.Children.length; i++ ) {
ids.concat( marketingListsViewModel.getIds( item.Children[i] ) )
}
}
return ids;
}
现在请记住,我并不打算在这里写javascript,但你明白了。您可能需要编写数组连接函数或扩展jQuery或类似的东西。
希望有所帮助