javascript - 如何检测一个项目在 knockout observable 数组之间的移动

标签 javascript jquery jquery-ui knockout.js

我正在使用查询和 knockout 构建一个 web-abb。 在某个页面上,我有几个项目列表。 这些列表是可排序和连接的,所以就 JQuery 而言,一切正常。我使用 Ryan Niemeyer 的示例为可排序对象创建自定义绑定(bind)以更新我的 View 模型的可观察数组。

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

如果其中一个调用出现问题,则后端将处于无效状态。

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

最佳答案

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

这里是我为 KO 2.0 使用的绑定(bind)可能看起来像一个回调:

//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
};

然后您可以像这样指定绑定(bind):

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

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

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

关于javascript - 如何检测一个项目在 knockout observable 数组之间的移动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8961707/

相关文章:

javascript - 在FullCalendar dayClick事件中,能否获取点击日期已经存在的事件?

javascript - 从 jQuery 对象中移除元素

javascript - 预编译模板的 Dust.js 客户端渲染问题

javascript - Fabric.JS 和 AngularJS – 0 和 '0' 之间的区别

jQuery UI 选项卡未呈现

ruby-on-rails - 如何将jquery-ui添加到Ruby on Rails 3.1应用程序?

jquery - 动态表单字段上的 angularjs datepicker 指令 "Missing instance data"

javascript - 自页面加载后的 JQuery Clock/Timer(即使用户更改其计算机上的时间)

javascript - 无法使用 jQuery UI 插件的 destroy() 方法(+冲突的 jQuery 插件)

jquery - jquery设置幻灯片效果的方法