在多个 select 中上下移动元素不起作用

move up and down elements in a multiple select not working

我创建了一个包含多个 select 的 angularjs 应用程序,我在上面有向上和向下按钮,当我点击向上和向下按钮时,项目的相应移动应该在多个 select,我有一个用普通 javascript 完成的示例,它正确地做了类似的事情,如 fiddle 所示但是当我试图在 AngularJS 中实现相同的东西时,它无法正常工作

谁能告诉我一些解决方案

我的代码如下

JSFiddle

html

<div ng-app='myApp' ng-controller="ArrayController">
    <select id="select" size="9" ng-model="persons" ng-options="item as item.name for item in peoples | orderBy:'name'" multiple></select>
    <br/>
    <button ng-click="moveUp()">Up</button>
    <br/>
    <button ng-click="moveDown()">Down</button>
    <br/>
</div>

脚本

var app = angular.module('myApp', []);
app.controller('ArrayController', function ($scope) {
    $scope.peoples = [{
        name: 'Jacob'
    }, {
        name: 'Sunny'
    }, {
        name: 'Lenu'
    }, {
        name: 'Mathew'
    }, {
        name: 'Ferix'
    }, {
        name: 'Kitex'
    }];

    $scope.moveUp = function () {
        var select = document.getElementById("select");
        var i1=0, i2=1;
        while (i2 < select.options.length) {
            swapIf(select,i1++,i2++);
        }
    };

    $scope.moveDown = function () {
        var select = document.getElementById("select");
        var i1=select.options.length-1, i2=i1-1;
        while (i1 > 0) {
            swapIf(select,i1--,i2--);
        }
    };

    var swapVar = '';
    function swapIf(sel,i1,i2) {
        if ( ! select[i1].selected && select[i2].selected) {
            swapVar = select[i2].text;
            select[i2].text = select[i1].text;
            select[i1].text = swapVar;
            swapVar = select[i2].value;
            select[i2].value = select[i1].value;
            select[i1].value = swapVar;
            select[i1].selected = true;
            select[i2].selected = false;
        }
    }
});

Demo

您需要更新 swapIf 实现,以便它交换模型,而不是视图中的选项:

function swapIf(sel,i1,i2) {
    if ( ! select[i1].selected && select[i2].selected) {

        var obj1 = $scope.peoples[i1];
        var obj2 = $scope.peoples[i2];
        $scope.peoples[i2] = obj1;
        $scope.peoples[i1] = obj2;
        select[i1].selected = true;
        select[i2].selected = false;
    }
}

此外,删除视图中的 orderBy,并使用 $filter 服务在控制器中初始化排序。您需要这样做的原因是,只要用户单击 up/down 按钮,列表就会重新排序。

persons 将 return 列表中包含 select 任何项目的数组。一种解决方案是创建一个 for 循环,获取 persons 数组中每个项目的 indexOfsplice peoples 数组中的项目,increment/decrement 索引,splice 返回 peoples 数组。

这是一个新的 moveUp() 函数,可以向上移动多个 selected 项目:

   $scope.moveUp = function () {
        for(var i = 0; i < $scope.persons.length; i++) {
            var idx = $scope.peoples.indexOf($scope.persons[i])
            console.log(idx);
            if (idx > 0) {
                var itemToMove = $scope.peoples.splice(idx, 1)
                console.log(itemToMove[0])
                $scope.peoples.splice(idx-1, 0, itemToMove[0]);

            }
        }
    };

这是更新后的 moveDown() 函数:

   $scope.moveDown = function () {
        for(var i = 0; i < $scope.persons.length; i++) {
            var idx = $scope.peoples.indexOf($scope.persons[i])
            console.log(idx);
            if (idx < $scope.peoples.length) {
                var itemToMove = $scope.peoples.splice(idx, 1)
                console.log(itemToMove[0])
                $scope.peoples.splice(idx+2, 0, itemToMove[0]);

            }
        }
    };    

这是 Working Demo(效果不是很好,仅供参考 - 见下文)

此解决方案还保持了视图和控制器之间的分离。控制器负责处理数据,视图显示该数据。这样我们就可以避免任何不愉快的纠缠。 DOM 控制器内部的操作非常难以测试。

修改后编辑: 因此,我以前的解决方案在某些情况下有效,但在使用不同的 select 组合时会表现得很奇怪。经过一番挖掘,我发现有必要通过以下方式添加曲目:

<select id="select" size="9" ng-model="persons" ng-options="item as item.name for item in peoples track by item.name" multiple>

似乎 select 会 return 具有任意 select 离子顺序的 persons 对象,这把事情搞砸了,尤其是在你点击几次之后,它似乎对东西在哪里感到困惑。

此外,我不得不在向下移动项目时克隆和反转人员数组,因为在添加 track by item.name 时它 return 按顺序排列项目,但是如果您尝试遍历数组,将每一个向下移动,您可能会影响数组中其他项目的位置(进一步产生不可预测的行为)。所以当我们向下移动多个项目时,我们需要从底部开始,逐步向上。

这是一个解决方案,我似乎已经消除了在制作多个任意 select 离子时出现的任何不可预测的行为:

Working Demo

编辑: 我发现的一个错误是,当您将多个 selected 项目一直向上或向下移动,然后尝试再次将其移动到那个方向时,会发生奇怪的事情。任何不重新 selecting 的进一步移动都会产生不可预测的结果。

编辑: 之前编辑中提到的不可预测的行为是因为函数看到,虽然第一个项目在它的最终位置,但第二个、第三个、第四个等项目不在结束位置,因此它试图移动它们这导致对已经全部推到顶部或底部的项目进行疯狂的重新排序。为了解决这个问题,我设置了一个 var 来跟踪先前移动的项目的位置。如果发现当前项目在相邻位置,它会简单地将它留在那儿并继续前进。

最终函数看起来像这样:

$scope.moveUp = function () {
        var prevIdx = -1;
        var person = $scope.persons.concat();
        console.log($scope.persons);
        for(var i = 0; i < $scope.persons.length; i++) {
            var idx = $scope.peoples.indexOf($scope.persons[i])
            console.log(idx);
            if (idx-1 === prevIdx) {
                prevIdx = idx
            } else if (idx > 0) {
                var itemToMove = $scope.peoples.splice(idx, 1)
                console.log(itemToMove[0])
                $scope.peoples.splice(idx-1, 0, itemToMove[0]);

            }
        }
    };

(希望如此)Final Demo

编辑: 我喜欢这个问题,并希望有一个更好的解决方案,以防出现重复的列表项。这很容易解决,方法是给数组中的每个对象一个唯一的 id 键,然后将 track by item.name 更改为 track by item.id,一切都像以前一样工作。

Working Demo for Duplicates