如何从Angular.js数组中删除元素/节点

72

我想从数组 $scope.items 中删除元素,以便在视图中 ng-repeat="item in items" 中删除项目。

以下是一些演示用代码:

for(i=0;i<$scope.items.length;i++){
    if($scope.items[i].name == 'ted'){
      $scope.items.shift();
    }
}

如果视图中有名称为Ted,我想要删除第一个元素。虽然它可以正常运行,但是因为所有的数组键都发生了移动,所以视图重新加载了所有元素,这在我创建的移动应用程序中造成了不必要的延迟。

是否有人对此问题有解决方案?


我已经成功使用splice修改了一个在ng-repeat中使用的数组,没有任何奇怪的副作用。 - BoxerBucks
看起来items是一个数组的数组,或者你不能调用items[i].shift(); - zs2020
嗨,感谢回复。抱歉,我的问题代码中有个错别字,我刚刚更新了它。 - TheNickyYo
那为什么要移除数组的第一个元素而不是位置 i 处的元素呢? - zs2020
只需在控制器中添加 $filter,3行代码就能解决你的问题。 - Maxim Shoustin
可能是重复的问题,参考如何使用ng-click从数组中删除项目或对象? - azerafati
12个回答

138

从数组中删除元素没有什么复杂的技巧。想要从任何数组中删除元素,你需要使用splice方法:$scope.items.splice(index, 1);。这里有一个示例

HTML

<!DOCTYPE html>
<html data-ng-app="demo">
  <head>
    <script data-require="angular.js@1.1.5" data-semver="1.1.5" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>
  <body>
    <div data-ng-controller="DemoController">
      <ul>
        <li data-ng-repeat="item in items">
          {{item}}
          <button data-ng-click="removeItem($index)">Remove</button>
        </li>
      </ul>
      <input data-ng-model="newItem"><button data-ng-click="addItem(newItem)">Add</button>
    </div>
  </body>
</html>

JavaScript

"use strict";

var demo = angular.module("demo", []);

function DemoController($scope){
  $scope.items = [
    "potatoes",
    "tomatoes",
    "flour",
    "sugar",
    "salt"
  ];

  $scope.addItem = function(item){
    $scope.items.push(item);
    $scope.newItem = null;
  }

  $scope.removeItem = function(index){
    $scope.items.splice(index, 1);
  }
}

(索引,1)中的“1”是什么意思? - ShibinRagh
1
@ShibinRagh,请阅读文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/splice - madhead
24
请注意,$index 不一定与 $scope.items 的索引相匹配。特别是当您使用 orderByfilter 时。我认为使用 $index 是不可伸缩的,并且可能很危险。 - Antoine Pinsard
@AntoinePinsard,你有什么其他建议吗? - stackPusher
@ShibinRagh splice() 中的 1 表示仅从索引位置开始删除一个元素。 - stackPusher
1
@stackPusher,如果我没记错的话,我使用了bpaul的解决方案https://dev59.com/N2Ml5IYBdhLWcg3wkXpx#22160628。 - Antoine Pinsard

124

对于任何回到这个问题的人。正确的“Angular方式”从数组中删除项目是使用 $filter。只需将 $filter 注入您的控制器,然后执行以下操作:

对于任何回到这个问题的人。从数组中删除项的正确“Angular方式”是使用 $filter。只需将 $filter 注入您的控制器,然后执行以下操作:

$scope.items = $filter('filter')($scope.items, {name: '!ted'})

您不需要加载任何额外的库或使用JavaScript原语。


如果你有一个名为“ted”和“teddy”的元素,并且只想删除“ted”元素,这会起作用吗? - Jervelund
2
@Jervelund 是的,你可以在结尾处使用 true 来实现:$filter('filter')($scope.items, {name: '!ted'}, true),请参见 https://docs.angularjs.org/api/ng/filter/filter。 - Betty St
4
我认为这个答案应该被采纳,因为它的角度很好。 - MrBoJangles
有没有可能使用过滤器来删除包含特定数字ID的元素?$filter('filter')($scope.items, {id: 42}, true); 可以找到该元素,但我还没有找到否定它的方法,因为 {id: "!42"} 是一个字符串而不是数字。 - Oscar
29
иҝҷжҳҜзҝ»иҜ‘з»“жһңпјҡиҝҷдёӘ $filter('filter')($scope.items, function(value, index) {return value.id !== 42;}); жҳҜжңүж•Ҳзҡ„гҖӮ ж¶ҰиүІеҗҺзҡ„зҝ»иҜ‘пјҡиҝҷж®өд»Јз ҒеҸҜд»ҘиҝҮж»ӨжҺү $scope.items ж•°з»„дёӯ id дёҚзӯүдәҺ 42 зҡ„е…ғзҙ гҖӮ - Oscar
尽管这个答案很“Angular”,但与splice方法相比,它的可读性/可用性要低得多。 - A_funs

16

你可以使用纯粹的JavaScript - Array.prototype.filter()

$scope.items = $scope.items.filter(function(item) {
    return item.name !== 'ted';
});

11
因为当你在数组上执行 shift() 时,它会改变数组的长度。所以for循环会出错。你可以从后往前遍历以避免这个问题。
顺便说一下,我假设你想要删除位置 i 上的元素而不是数组中的第一个元素。(你代码中的 $scope.items.shift(); 会删除数组的第一个元素)
for(var i = $scope.items.length - 1; i >= 0; i--){
    if($scope.items[i].name == 'ted'){
        $scope.items.splice(i,1);
    }
}

8

这里有一个使用 Underscore 库filter 函数可能会对您有帮助,我们可以删除名称为 "ted" 的项目。

$scope.items = _.filter($scope.items, function(item) {
    return !(item.name == 'ted');
 });

2
这就是“Angular”的方式!当然,它可以改进:$scope.items = $filter('filter').filter($scope.items, {name: '!ted'}) - bpaul
3
@bpaul的改进不错,但似乎有一个错误,应该是:$scope.items = $filter('filter')($scope.items, {name: '!ted'}) - Eugene
1
@Eugene 一点也不。我使用了Underscore库,已经添加了链接。 - Maxim Shoustin
谢谢@Eugene,那是个打字错误。我应该在发布之前测试代码!此外,这应该是一个答案,因为它不需要下划线(或任何其他库),并且是惯用的Angular。我会添加它。 - bpaul

7

我喜欢@madhead提供的解决方案。

然而,我的问题是它不能用于已排序的列表。因此,我没有将索引传递给删除函数,而是传递了项,然后通过indexof获取索引。

例如:

var index = $scope.items.indexOf(item);
$scope.items.splice(index, 1);

以下是madheads示例的更新版本: 示例链接 HTML
<!DOCTYPE html>
<html data-ng-app="demo">
  <head>
    <script data-require="angular.js@1.1.5" data-semver="1.1.5" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>
  <body>
    <div data-ng-controller="DemoController">
      <ul>
        <li data-ng-repeat="item in items|orderBy:'toString()'">
          {{item}}
          <button data-ng-click="removeItem(item)">Remove</button>
        </li>
      </ul>
      <input data-ng-model="newItem"><button data-ng-click="addItem(newItem)">Add</button>
    </div>
  </body>
</html>

JavaScript

"use strict";

var demo = angular.module("demo", []);

function DemoController($scope){
  $scope.items = [
    "potatoes",
    "tomatoes",
    "flour",
    "sugar",
    "salt"
  ];

  $scope.addItem = function(item){
    $scope.items.push(item);
    $scope.newItem = null;
  }

  $scope.removeItem = function(item){
    var index = $scope.items.indexOf(item);
    $scope.items.splice(index, 1);
  }
}

6

稍微扩展一下“angular”解决方案。我想根据其数字 ID 排除一个项目,因此“!”方法不起作用。 更通用的解决方案适用于 { name: 'ted' } 或 { id: 42 }:

mycollection = $filter('filter')(myCollection, { id: theId }, function (obj, test) { 
                                                             return obj !== test; });

2

我对此的解决方案(没有引起任何性能问题):

  1. 扩展数组对象,添加一个名为“remove”的方法(我相信你将不止一次需要它):
Array.prototype.remove = function(from, to) {
  var rest = this.slice((to || from) + 1 || this.length);
  this.length = from < 0 ? this.length + from : from;
  return this.push.apply(this, rest);
};

我在所有的项目中都使用它,感谢John Resig John Resig的网站

  1. 使用forEach和基本检查:
$scope.items.forEach(function(element, index, array){
          if(element.name === 'ted'){
              $scope.items.remove(index);
          }
        });

在AngularJS中,$digest结束后,我的UI会立即更新,而且没有任何明显的延迟。


在迭代列表时删除元素真的安全吗? - Volte
一般来说,你应该小心地处理这个问题。我认为使用 .forEach 而不是 for 循环可以使它变得更安全,因为 MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach 上说:“forEach() 方法按升序为数组中含有效值的每个元素执行一次 callback 函数,那些已删除或从未被赋值的项将被跳过(例如在稀疏数组上)。” 因此,在访问前或访问后删除的项目只是“被忽略”,这在我看来使其对于这种情况更加安全。 - Fer To
如果你需要移除超过1个元素,则在当前格式下是不安全的。在第一次移除后,index仍将反映原始数组中的索引,但在当前$scope.items中相同项的索引将对已移除的每个前置项减少一个值。因此,虽然使用forEach迭代时删除项目是安全的,但在回调中提供的索引使用它则是不安全的。 - Chris Schaller
这是真的。如果要删除一个项目,我建议使用.reduce或.filter的混合。在这两种情况下,您都需要将新数组分配给$scope.items。话虽如此,Angularjs已正式存档并终止生命周期。因此,任何阅读此内容的人,这段代码很可能已经过时了。 - Fer To

1
我的解决方案非常直接。
app.controller('TaskController', function($scope) {
 $scope.items = tasks;

    $scope.addTask = function(task) {
        task.created = Date.now();
        $scope.items.push(task);
        console.log($scope.items);
    };

    $scope.removeItem = function(item) {
        // item is the index value which is obtained using $index in ng-repeat
        $scope.items.splice(item, 1);
    }
});

1

在我的 REST 资源集合中使用 indexOf 函数效果不佳。

我不得不创建一个函数,用于检索资源集合中某个资源的数组索引:

factory.getResourceIndex = function(resources, resource) {
  var index = -1;
  for (var i = 0; i < resources.length; i++) {
    if (resources[i].id == resource.id) {
      index = i;
    }
  }
  return index;
}

$scope.unassignedTeams.splice(CommonService.getResourceIndex($scope.unassignedTeams, data), 1);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接