1

我有一个使用敲除实现的 n 级复选框树,其中父级 chechbox 的选择应选择其子级(但不是相反),一旦提交数据,所选元素的 id 应转换为 JSON 发送到服务器。

我无法弄清楚如何单向复选框关系,也无法弄清楚如何以以下方式过滤我的最终 json:

  1. 如果选择了一个父级(因为这意味着它的所有子级也被选中),则不在 json 中发送其子级 ID

  2. 当只选择一个或几个孩子时如何不发送父母 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>
4

2 回答 2

2

感谢你的回答。问题是,如果现在使用您的解决方案,如果服务器中的数据已经检查了父级,那么它的子级检查值不会发生变化。

我在模型中添加了以下内容来解决这个问题:

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>
于 2013-02-14T14:07:15.230 回答
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 或类似的东西。

希望能帮助到你

于 2013-02-13T20:42:12.523 回答