如何检测淘汰可观察数组之间的项目移动

发布于 2024-12-28 13:54:07 字数 308 浏览 0 评论 0原文

我正在使用查询和淘汰赛构建一个网络abb。 在某个页面上,我有几个项目列表。 这些列表是可排序和连接的,因此就 JQuery 而言,一切正常。我使用 Ryan Niemeyer 的示例为可排序对象创建自定义绑定,以更新我的视图模型的可观察数组。

这一切都很好,但我想将更改保存到后端服务器。 使用可观察数组上的订阅,我可以检测到某个项目已从数组中删除或添加到数组中,但这会导致对后端服务器的两次更新调用。

如果在这些调用之一期间出现问题,则后端将处于无效状态。

如何检测删除一项以及随后添加同一项,以便 Web 应用程序可以对后端服务器进行一次移动调用?

I am building a web-abb using query and knockout.
On a certain page I have a couple of lists of items.
These lists are sortable and connected, so as far as JQuery goes all is working OK. I have used Ryan Niemeyer's example to create a custom binding for the sortables to update my view-model's observable arrays.

This works all quite nicely but i want to save the changes to a back-end server.
Using subscriptions on the observable arrays i can detect that an item is removed from, and is added to the array but this leads to two update calls to the back-end server.

If something goes wrong during one of these calls then the backend will be in an invalid state.

How would one go about detecting that the removal of an item and subsequent adding of that same item so that the web-application can make one move-call to the back-end server?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一曲琵琶半遮面シ 2025-01-04 13:54:07

我认为处理这个问题的一个好方法是向 sortableList 绑定添加一个回调选项,该选项将项目、原始父级和新父级作为参数传递。

以下是我在 KO 2.0 中使用的带有回调的绑定可能的样子:

//connect items with observableArrays
ko.bindingHandlers.sortableList = {
    init: function(element, valueAccessor, allBindingsAccessor, data, context) {
        var options = ko.utils.unwrapObservable(valueAccessor());

        //attach the appropriate class to our element
        if (ko.bindingHandlers.sortableList.autoAddClass) {
             ko.utils.toggleDomNodeCssClass(element, ko.bindingHandlers.sortableList.defaultClass, true);   
        }

        $(element).data("sortList", options.list || valueAccessor()); //attach meta-data
        $(element).sortable({
            update: function(event, ui) {
                var item = ui.item.data("sortItem");
                if (item) {
                    //identify parents
                    var originalParent = ui.item.data("parentList");
                    var newParent = ui.item.parent().data("sortList");
                    //figure out its new position
                    var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
                    if (position >= 0) {
                        originalParent.remove(item);
                        newParent.splice(position, 0, item);
                    }
                    ui.item.remove();

                    if (options.afterDrop) {
                       options.afterDrop.call(this, item, newParent, originalParent);   
                    }
                }
            },
            connectWith: '.' + ko.bindingHandlers.sortableList.defaultClass
        });
        return ko.bindingHandlers.template.init.apply(this, arguments);
    },
    update: function(element, valueAccessor, allBindingsAccessor, data, context) {
       var options = ko.utils.unwrapObservable(valueAccessor()),
           newOptions = {}; 

        //build our options to pass to the template engine
        if (options.list) {
            newOptions.foreach = options.list;
            newOptions.name = options.tmpl;
            newOptions.includeDestroyed = options.includeDestroyed;
            newOptions.afterAdd = options.afterAdd;
            newOptions.beforeRemove = options.beforeRemove; 
        } else {
           newOptions.foreach = valueAccessor();
        }

        //use an afterRender function to add meta-data
        if (options.afterRender) {
            //wrap the existing function, if it was passed
            newOptions.afterRender = function(element, data) {
               ko.bindingHandlers.sortableList.afterRender.call(data, element, data);
               options.afterRender.call(data, element, data); 
            }  
        } else {
            newOptions.afterRender = ko.bindingHandlers.sortableList.afterRender;
        }
        //call the actual template binding
        ko.bindingHandlers.template.update(element, function() { return newOptions; }, allBindingsAccessor, data, context);  
    },
    afterRender: function(elements, data) {
        ko.utils.arrayForEach(elements, function(element) {
            if (element.nodeType === 1) {
                $(element).data("sortItem", data);
                $(element).data("parentList", $(element).parent().data("sortList"));
            } 
        });
    },
    defaultClass: 'container',
    autoAddClass: true
};

然后,您可以指定绑定,如下所示:

<ul data-bind="sortableList: { tmpl: 'myItems', list: myObservableArray, afterDrop: myCallback }"></ul>

现在,您可以添加自己的回调,告诉服务器该项目已移动。 observableArrays 是函数(它们是对象),因此您实际上可以为它们分配属性。这是一个示例,我为每个可观察数组分配了一个 id 属性,然后在回调中访问它,以便我有一种友好的方式知道哪个是旧父级,哪个是新父级:

http://jsfiddle.net/rniemeyer/QZscP/

I think that a good way to handle this would be to add a callback option to the sortableList binding that passes the item, original parent, and new parent as arguments.

Here is what the binding that I am using for KO 2.0 might look like with a callback:

//connect items with observableArrays
ko.bindingHandlers.sortableList = {
    init: function(element, valueAccessor, allBindingsAccessor, data, context) {
        var options = ko.utils.unwrapObservable(valueAccessor());

        //attach the appropriate class to our element
        if (ko.bindingHandlers.sortableList.autoAddClass) {
             ko.utils.toggleDomNodeCssClass(element, ko.bindingHandlers.sortableList.defaultClass, true);   
        }

        $(element).data("sortList", options.list || valueAccessor()); //attach meta-data
        $(element).sortable({
            update: function(event, ui) {
                var item = ui.item.data("sortItem");
                if (item) {
                    //identify parents
                    var originalParent = ui.item.data("parentList");
                    var newParent = ui.item.parent().data("sortList");
                    //figure out its new position
                    var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
                    if (position >= 0) {
                        originalParent.remove(item);
                        newParent.splice(position, 0, item);
                    }
                    ui.item.remove();

                    if (options.afterDrop) {
                       options.afterDrop.call(this, item, newParent, originalParent);   
                    }
                }
            },
            connectWith: '.' + ko.bindingHandlers.sortableList.defaultClass
        });
        return ko.bindingHandlers.template.init.apply(this, arguments);
    },
    update: function(element, valueAccessor, allBindingsAccessor, data, context) {
       var options = ko.utils.unwrapObservable(valueAccessor()),
           newOptions = {}; 

        //build our options to pass to the template engine
        if (options.list) {
            newOptions.foreach = options.list;
            newOptions.name = options.tmpl;
            newOptions.includeDestroyed = options.includeDestroyed;
            newOptions.afterAdd = options.afterAdd;
            newOptions.beforeRemove = options.beforeRemove; 
        } else {
           newOptions.foreach = valueAccessor();
        }

        //use an afterRender function to add meta-data
        if (options.afterRender) {
            //wrap the existing function, if it was passed
            newOptions.afterRender = function(element, data) {
               ko.bindingHandlers.sortableList.afterRender.call(data, element, data);
               options.afterRender.call(data, element, data); 
            }  
        } else {
            newOptions.afterRender = ko.bindingHandlers.sortableList.afterRender;
        }
        //call the actual template binding
        ko.bindingHandlers.template.update(element, function() { return newOptions; }, allBindingsAccessor, data, context);  
    },
    afterRender: function(elements, data) {
        ko.utils.arrayForEach(elements, function(element) {
            if (element.nodeType === 1) {
                $(element).data("sortItem", data);
                $(element).data("parentList", $(element).parent().data("sortList"));
            } 
        });
    },
    defaultClass: 'container',
    autoAddClass: true
};

You would then specify the binding like:

<ul data-bind="sortableList: { tmpl: 'myItems', list: myObservableArray, afterDrop: myCallback }"></ul>

Now, you can add your own callback that tells the server that the item was moved. observableArrays are functions (which are objects), so you can actually assign properties to them. Here is a sample where I assigned an id property to each observable array and then access it in the callback, so that I have a friendly way of knowing which one was the old parent and which was the new parent:

http://jsfiddle.net/rniemeyer/QZscP/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文