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

时间:2015-04-18 05:22:57

标签: javascript html angularjs select dropdownbox

我创建了一个带有多个选择的angularjs应用程序,我有上下按钮,当我点击上下按钮时,相应的项目移动应该在多个选择内完成,我有一个样本的东西,有已经使用正常的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;
        }
    }
});

2 个答案:

答案 0 :(得分:7)

persons将返回列表中所选项目的数组。一种解决方案是创建一个for循环,获取indexOf数组中每个项的personssplice表示peoples数组中的项,递增/递减索引,splice表示peoples数组。

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

   $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 id="select" size="9" ng-model="persons" ng-options="item as item.name for item in peoples track by item.name" multiple>

似乎select会返回带有任意选择顺序的persons对象,这会让事情搞得一团糟,特别是在你点击几次后,它似乎对事情的位置感到困惑。

此外,我在向下移动项目时必须克隆并反转人员数组,因为在添加track by item.name时,它会按顺序返回项目,但如果您尝试迭代数组,将每个项目向下移动,则可能会影响数组中其他项的位置(进一步产生不可预测的行为)。所以我们需要从底部开始,在向下移动多个项目时继续努力。

这是一个解决方案,在进行多项任意选择时,我似乎已经消除了任何不可预测的行为:

Working Demo

修改 我发现的一个错误是,当您将多个选定项目一直向上或向下移动时会发生奇怪的事情,然后再尝试将其向该方向移动一次。没有重新选择的任何进一步运动都会产生不可预测的结果。

修改 上一次编辑中提到的不可预测的行为是因为函数看到了这一点,虽然第一项是在它的最终位置,但是第二,第三,第四等项目​​不在最终位置,因此它试图移动它们导致疯狂地重新排序已经将所有项目推到顶部或底部的项目。为了解决这个问题,我设置了一个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

答案 1 :(得分:5)

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服务初始化控制器中的排序。您需要执行此操作的原因是,只要用户单击向上/向下按钮,就会重新排序列表。