【问题标题】:move up and down elements in a multiple select not working在多项选择中上下移动元素不起作用
【发布时间】:2015-06-25 03:15:05
【问题描述】:

我创建了一个带有多重选择的 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;
        }
    }
});

【问题讨论】:

  • 可能是orderBy:'name' 导致问题
  • @pankajparkar 我们可以在不使用document.getElementById 的情况下用不同的方法实现这一点吗,您对此有什么想法吗
  • 这个控制器代码是不好的角度实践。 DOM 操作应该由指令完成,而不是控制器。
  • @tpie 是的,你是对的....我正在考虑如何将代码简化为特定角度的代码
  • @tpie 如果选择多个,则更难操作

标签: javascript html angularjs select dropdownbox


【解决方案1】:

persons 将返回列表中选择的任何项目的数组。一种解决方案是创建一个 for 循环,以获取 persons 数组中每个项目的 indexOfsplice 将项目从 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(效果不太好,仅供参考 - 见下文)

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

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

&lt;select id="select" size="9" ng-model="persons" ng-options="item as item.name for item in peoples track by item.name" multiple&gt;

似乎 select 会返回带有任意选择顺序的 persons 对象,这会搞砸事情,尤其是在您单击几次之后,它似乎对事情的位置感到困惑。

此外,我必须在向下移动项目时克隆和反转 people 数组,因为添加 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

【讨论】:

  • 您的解决方案有效,但一个问题是说我选择LenuFerix,然后当它们到达顶部时以Lenu 作为第一位置时单击向上按钮Ferix 作为第二个位置,如果我们再次单击向上按钮,订单将更改为 Ferix 作为第一个位置,Lenu 作为第二个位置,同样在最初选择所有选项并单击向上按钮你可以看到一些发生意外排序
  • 是的,我也注意到了这一点。这是因为该函数的工作方式是获取任何选定项目的位置,并递减索引。在选择超过一两个项目并向下移动时,我还发现了一些奇怪的行为。正在努力...
  • 该问题的任何解决方案@tpie,我也从我身边尝试过......但没有通过
  • 检查更新的代码..我消除了很多奇怪的行为......不过在最后一个问题上工作。
  • 好的,知道了。现在应该没有错误了。
【解决方案2】:

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

【讨论】:

  • 它适用于单选,但似乎不适用于多值选择
  • 再试一次 - 我已经解决了。
  • 这不是角度方式。
  • IS 角度方式:P 它使用与原始帖子相同的算法解决了 OPs 问题(顺便说一句,这很酷)。
  • @pixelbits 你的代码工作正常,但就像我正在寻找基于角度的东西
猜你喜欢
  • 2015-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-12
  • 2015-05-09
  • 1970-01-01
  • 2018-12-08
相关资源
最近更新 更多