【问题标题】:How do I swap two items in an observableArray?如何交换 observableArray 中的两个项目?
【发布时间】:2012-05-02 17:44:06
【问题描述】:

我有一个按钮,可以将一个项目在 observableArray 中向左移动一个位置。我正在按照以下方式进行操作。但是,缺点是 categories()[index] 会从数组中删除,因此会丢弃该节点上的任何 DOM 操作(在我的情况下通过 jQuery 验证)。

有没有办法在不使用临时变量的情况下交换两个项目以保留 DOM 节点?

    moveUp: function (category) {
        var categories = viewModel.categories;
        var length = categories().length;
        var index = categories.indexOf(category);
        var insertIndex = (index + length - 1) % length;

        categories.splice(index, 1);
        categories.splice(insertIndex, 0, category);
        $categories.trigger("create");
    }

【问题讨论】:

    标签: knockout.js


    【解决方案1】:

    这是我的moveUp 版本,可以一步完成交换:

    moveUp: function(category) {
        var i = categories.indexOf(category);
        if (i >= 1) {
            var array = categories();
            categories.splice(i-1, 2, array[i], array[i-1]);
        }
    }
    

    但这仍然不能解决问题,因为 Knockout 仍会将交换视为删除和添加操作。不过,Knockout 有一个 open issue 来支持移动项目。 更新:从 2.2.0 版开始,Knockout 可以识别移动的项目,foreach 绑定不会重新渲染它们。

    【讨论】:

    【解决方案2】:

    我知道这个答案来得有点晚,但我认为它可能对其他想要更通用交换解决方案的人有用。您可以像这样向 observableArrays 添加交换函数:

    ko.observableArray.fn.swap = function(index1, index2) {
        this.valueWillMutate();
    
        var temp = this()[index1];
        this()[index1] = this()[index2];
        this()[index2] = temp;
    
        this.valueHasMutated();
    }
    

    然后,您可以使用此函数在给定索引的情况下交换数组中的两个元素:

    myArray.swap(index1, index2);
    

    对于 moveUp 函数,您可以执行以下操作:

    moveUp: function(category) {
        var i = categories.indexOf(category);
        if (i > 0) {
            categories.swap(i, i+1);
        }
    }
    

    【讨论】:

    • 像魅力一样工作,即使在 IE 11 中也是如此。
    【解决方案3】:

    我有一个类似的问题,因为我想在我的项目上使用 jQuery 拖放。 我的解决方案变成使用 knockoutjs 模板将 beforeRemove 和 afterAdd 事件绑定到模型。 Person 类/函数也是一个简单的淘汰视图模型。

    在下面的示例中,我使用 .draggable(),但您可以轻松使用验证。添加您自己的代码来操作 observableArray,您应该一切顺利。

    HTML:

    <div data-bind="template: {foreach:attendeesToShow, beforeRemove:hideAttendee, afterAdd:showAttendee}">
        <div class="person">
            <img src="person.jpg" alt="" />
            <div  data-bind="text: firstName" ></div>
            <div class="deleteimg" data-bind="click:$parent.removeAttendee" title="Remove"></div>
        </div>
    </div>
    

    视图模型:

    var ViewModel = function () {
        var self = this;
        var at = [new Person('First', 'Person', 'first@example.com'),
                        Person('Second', 'Person', 'second@example.com')
                    ];
        self.attendees = ko.observableArray(at);
    
        self.removeAttendee = function (attendee) {
            self.attendees.remove(attendee);
        };
    
        this.showAttendee = function (elem) {
            if (elem.nodeType === 1) {
        $(elem).hide().show("slow").draggable();//Add jQuery functionality 
            }
        };
        this.hideAttendee = function (elem) {
            if (elem.nodeType === 1) {
                $(elem).hide(function () {
                    $(elem).remove();
                });
            }
        };
    };
    
    ko.applyBindings(new ViewModel());
    

    【讨论】:

    • 能详细点吗?我正在尝试做的是使用按钮来允许用户进行排序(以后可能会实现拖放)。处理 beforeRemove、afterAdd 如何保留 DOM 元素? DOM 元素是由 Knockout 生成的,但被 jQuery 验证更改了,我通过删除然后重新插入项目而丢失了更改。
    • 在我的示例中,当项目重新添加到 DOM 时,我只是重新添加可拖动项,在您的情况下进行验证。在我的情况下,它不需要保留任何状态变量(它们都在模型中),所以它对我有用。验证不应该也一样吗?
    【解决方案4】:

    感谢 Michael Best 他的 moveup 版本

    我的 moveDown 版本

    moveDown: function(category) {
        var array = categories();
        var i = categories.indexOf(category);
        if (i < arr.length) {
            categories.splice(i, 2, array[i + 1], array[i]);
        }
    }
    

    【讨论】:

    • 您的版本不能正常工作。用“array.length - 1”替换“arr.length”就可以了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-06
    • 2022-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-01
    相关资源
    最近更新 更多