如何使用ng-click从数组中删除项目或对象?

266

我正在尝试编写一个函数,使我能够在按钮被点击时删除一个项目,但我认为我对该函数感到困惑了 - 我是否要使用$digest

HTML和 app.js:

<ul ng-repeat="bday in bdays">
  <li>
    <span ng-hide="editing" ng-click="editing = true">{{bday.name}} | {{bday.date}}</span>
    <form ng-show="editing" ng-submit="editing = false">
      <label>Name:</label>
      <input type="text" ng-model="bday.name" placeholder="Name" ng-required/>
      <label>Date:</label>
      <input type="date" ng-model="bday.date" placeholder="Date" ng-required/>
      <br/>
      <button class="btn" type="submit">Save</button>
      <a class="btn" ng-click="remove()">Delete</a>
    </form>
  </li>
</ul>

$scope.remove = function(){
  $scope.newBirthday = $scope.$digest();
};

2
你不需要使用 $digest,因为它用于进入 Angular 的 digest 循环(而且由于 ng-click,你已经在一个 digest 循环中了)。你是想从数组中删除一个项目吗? - Mark Rajcok
@MarkRajcok :) 是的,这就是我正在尝试做的。 - Jess McKenzie
ng-click 中的 remove() 没有上下文。需要更多的标记来显示正在被删除的内容,以及它是否在 ng-repeat 中,或者被删除的项目来自哪里,或者您想要从 remove() 中获得什么行为。 - charlietfl
@charlietfl 在ng-repeat中,我已经更新了问题。 - Jess McKenzie
12个回答

564
从数组中删除项目,可以在标记中将 bday 项传递给您的删除函数。然后在控制器中查找项目的索引并从数组中删除。
<a class="btn" ng-click="remove(item)">Delete</a>

然后在控制器中:

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

Angular会自动检测到bdays数组的变化并更新ng-repeat

演示:http://plnkr.co/edit/ZdShIA?p=preview

编辑:如果需要与服务器进行实时更新,则可以使用创建的$resource服务来管理数组更新,同时更新服务器。


62
如果您的列表在模板上进行了过滤,直接使用 $index 可能会产生错误。这是一个模板问题;更安全的做法是使用 ng-click='remove(bday)' 而不是 arr.splice(arr.indexOf(bday),1); - Umur Kontacı
6
您不需要传入 $index,因为您可以在方法内部使用“this”。$scope.remove = function() { $scope.bdays.splice(this.$index, 1); } - matchdav
1
@matthewdavidson,“this is undefined”。是Plunker/jsfiddle的问题吗? - Tjorriemorrie
11
如果未找到,.indexOf(item)会返回-1,如果你没有进行检查,这可能导致删除数组末尾的项。 - Ben Wilde
1
@ShibinRagh 阅读 Array.prototype.splice() 的文档。 - charlietfl
显示剩余9条评论

54

这是一个正确的答案:

<a class="btn" ng-click="remove($index)">Delete</a>
$scope.remove=function($index){ 
  $scope.bdays.splice($index,1);     
}

在@charlietfl的回答中,我认为他是错的,因为你将$index作为参数传递,但在控制器中使用了"wish"。如果我错了,请纠正我:)


看起来两个答案都是等价的,尽管你的函数可以接受没有$符号的索引,但依然能正常工作。 - svarog
18
如果你的ng-repeat中有orderBy或filter,这将无法起作用。 - Joan-Diego Rodriguez
如果您使用了track by $index,这将更有效。 - Ankit Balyan
@Joan-DiegoRodriguez 如果你确实有一个筛选器/排序方式,你会如何让它工作?没关系,只需阅读XMLilley的回答即可。 - jamesmstone
@AnkitBalyan 即使使用 track by $index,这也不起作用。 - Astitva Srivastava
显示剩余2条评论

29

如果你正在使用ng-repeat

你可以使用一行代码选项

    <div ng-repeat="key in keywords"> 
        <button ng-click="keywords.splice($index, 1)">

            {{key.name}}
        </button>
    </div>

$index被Angular用来显示在ng-repeat中数组的当前索引。


1
我喜欢并使用这个一行代码。 - etoricky

24

在基本情况下,使用$index非常完美,@charlietfl的答案很好。但有时候,$index是不够的。

想象一下,你有一个单一的数组,你要在两个不同的ng-repeat中呈现它。其中一个ng-repeat被筛选出具有真值属性的对象,另一个被筛选出具有伪造属性的对象。正在呈现两个不同的已过滤的数组,这些数组源自单个原始数组。 (或者,如果有助于可视化:也许您有一个人的单个数组,并且您想为该数组中的女性和男性分别创建一个ng-repeat。)您的目标:可靠地从原始数组中删除,使用来自过滤数组成员的信息。

在这些过滤数组中,$index将不是原始数组中项目的索引。它将是过滤子数组中的索引。因此,您将无法告诉该人在原始people数组中的索引,您只知道来自womenmen子数组的$index。尝试使用它进行删除,您将发现除了您想要的地方外,所有项目都会消失。该怎么办?

如果您足够幸运地使用数据模型包括每个对象的唯一标识符,则请使用该标识符而不是$index,查找对象并将其从主数组中splice。 (使用我的以下示例,但使用该唯一标识符。)但如果您没有那么幸运呢?

实际上,Angular会增强ng-repeated数组中(在主原始数组中)的每个项目,具有称为$$hashKey的唯一属性。您可以搜索原始数组以匹配要删除的项目的$$hashKey,并通过这种方式摆脱它。

注意,$$hashKey是实现细节,未包含在ng-repeat的发布API中。他们随时可以取消对该属性的支持。但可能不会。:-)

$scope.deleteFilteredItem = function(hashKey, sourceArray){
  angular.forEach(sourceArray, function(obj, index){
    // sourceArray is a reference to the original array passed to ng-repeat, 
    // rather than the filtered version. 
    // 1. compare the target object's hashKey to the current member of the iterable:
    if (obj.$$hashKey === hashKey) {
      // remove the matching item from the array
      sourceArray.splice(index, 1);
      // and exit the loop right away
      return;
    };
  });
}

使用以下方式调用:

ng-click="deleteFilteredItem(item.$$hashKey, refToSourceArray)"

编辑:使用像这样以$$hashKey为键而不是特定于模型的属性名称的函数,还具有使该函数可在不同模型和上下文中重复使用的显着附加优势。将其提供给数组引用和项目引用,它应该可以正常工作。



10

我通常以这种方式写作:

<a class="btn" ng-click="remove($index)">Delete</a>


$scope.remove = function(index){
  $scope.[yourArray].splice(index, 1)
};

希望这能帮到你。 在 $scope 和 [yourArray] 之间需要使用点(.)。

在 (index, 1) 中,“1”的意思是什么? - ShibinRagh
@ShibinRagh 这是 deleteCount。一个整数,表示要删除的旧数组元素数量。如果 deleteCount 为 0,则不会删除任何元素。在这种情况下,您应该至少指定一个新元素。如果 deleteCount 大于从 start 开始的数组中剩余的元素数量,则将删除数组末尾的所有元素。Array.prototype.splice() 文档 - ᴍᴀᴛᴛ ʙᴀᴋᴇʀ

9
在已接受的答案基础上,这个方法可以与ngRepeatfilter一起使用,并且更好地处理异常情况: 控制器:
vm.remove = function(item, array) {
  var index = array.indexOf(item);
  if(index>=0)
    array.splice(index, 1);
}

视图:

ng-click="vm.remove(item,$scope.bdays)"

你没有在控制器中将“remove”分配给$scope.vm,因此这段代码无法工作。 现在如果你这样做... $scope.vm = {remove: function(){...}},那么它就能工作了。 - Justin Russo

4

无控制器实现。

<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>

<script>
  var app = angular.module("myShoppingList", []); 
</script>

<div ng-app="myShoppingList"  ng-init="products = ['Milk','Bread','Cheese']">
  <ul>
    <li ng-repeat="x in products track by $index">{{x}}
      <span ng-click="products.splice($index,1)">×</span>
    </li>
  </ul>
  <input ng-model="addItem">
  <button ng-click="products.push(addItem)">Add</button>
</div>

<p>Click the little x to remove an item from the shopping list.</p>

</body>
</html>

splice()方法可以向数组中添加/删除元素。
array.splice(index, howmanyitem(s), item_1, ....., item_n)

索引:必需。一个整数,用于指定要添加/删除项的位置。使用负值以从数组末尾指定位置。

howmanyitem(s):可选。要删除的项数。如果设置为0,则不会删除任何项。

item_1, ..., item_n:可选。要添加到数组中的新项。


1
这是正确的答案。为什么要依赖控制器执行简单的JavaScript调用呢? - Elle Fie

3
我不赞同在控制器上调用方法。实际功能应使用服务,可扩展性和模块化应该通过定义指令来实现。您可以将调用注入到指令的点击事件中。例如,在您的HTML中...
<a class="btn" ng-remove-birthday="$index">Delete</a>

然后,创建一个指令...
angular.module('myApp').directive('ngRemoveBirthday', ['myService', function(myService){
    return function(scope, element, attrs){
        angular.element(element.bind('click', function(){
            myService.removeBirthday(scope.$eval(attrs.ngRemoveBirthday), scope);  
        };       
    };
}])

然后在你的服务中...

angular.module('myApp').factory('myService', [function(){
    return {
        removeBirthday: function(birthdayIndex, scope){
            scope.bdays.splice(birthdayIndex);
            scope.$apply();
        }
    };
}]);

当你像这样适当编写代码时,您将使未来的更改变得非常容易,而无需重新构造代码。它被正确组织,并且您正在使用自定义指令正确地处理自定义单击事件。
例如,如果您的客户说:“嘿,现在让它调用服务器并制作面包,然后弹出模态框。” 您只需轻松转到服务本身,而无需添加或更改任何HTML和/或控制器方法代码。如果您只有一个控制器上的一行,您最终需要使用服务来扩展客户要求的更大工作量的功能。
此外,如果您需要在其他地方放置另一个“删除”按钮,您现在有一个指令属性('ng-remove-birthday')可以轻松分配给页面上的任何元素。这现在使其成为模块化和可重复使用的。在处理Angular 2.0的HEAVY web组件范例时,这将非常方便。2.0中没有控制器。 :)
开心开发!!!

1

0
如果您的项目中有ID或任何特定字段,您可以使用filter()函数。它的作用类似于Where()函数。
<a class="btn" ng-click="remove(item)">Delete</a>

在控制器中:

$scope.remove = function(item) { 
  $scope.bdays = $scope.bdays.filter(function (element) {
                    return element.ID!=item.ID
                });
}

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