AngularJS ngRepeat element removal

68,311

Solution 1

You could create a generic remove method that would take in the array and the item to remove.

<div ng-app="" ng-controller="MyController">
    <div ng-repeat="email in emails">{{ email }} <a ng-click="remove(emails, $index)">Remove</a>
    </div>
    <div ng-repeat="phone in phones">{{ phone }} <a ng-click="remove(phones, $index)">Remove</a>
    </div>
</div>

$scope.remove = function(array, index){
    array.splice(index, 1);
}

Solution 2

No JS

<div ng-repeat="option in options" ng-init=options=[1,2,3,4,5]>
   <button ng-click="options.splice($index,1)">Remove me</button>      
</div>

Solution 3

<div ng-app="" ng-controller="MyController">
    <div ng-repeat="email in emails as datasource">{{ email }} 
        <a ng-click="datasource.splice($index,1)">Remove</a>
    </div>
    <div ng-repeat="phone in phones as datasource">{{ phone }} 
        <a ng-click="datasource.splice($index,1)">Remove</a>
    </div>
</div>

Solution 4

A very simple and convenient way that works cross-browser is to use the 'remove' utility method from the library lodash.

<div ng-repeat="phone in phones">{{ phone }} 
  <a ng-click="removeItem(phones, phone)">Remove</a>
</div>

In your controller you declare then

//inject lodash dependency

//declare method in scope
$scope.removeItem = function(list, item){
   lodash.remove(list,function(someItem) { return item === someItem});
}

You may of course use indexes if you like. See https://lodash.com/docs#remove

Solution 5

If you have used ng-repeat on an object instead of an array, do the following.

<div ng-app="" ng-controller="MyController">
    <div ng-repeat="email in emails">{{ email }} 
      <a ng-click="remove(emails, email)">Remove</a>
    </div>
    <div ng-repeat="phone in phones">{{ phone }} 
      <a ng-click="remove(phones, phone)">Remove</a>
    </div>
</div>

$scope.remove = function(objects, o){
    delete object[o.id];
}

or the more terse

<div ng-app="" ng-controller="MyController">
    <div ng-repeat="email in emails">{{ email }} 
      <a ng-click="delete emails[email.id]">Remove</a>
    </div>
    <div ng-repeat="phone in phones">{{ phone }} 
      <a ng-click="delete phones[phone.id]">Remove</a>
    </div>
</div>

presumes that the objects look like this

var emails = {  '123' : { id : '123', .... }  };

var phones = {  '123' : { id : '123', .... }  };
Share:
68,311
Admin
Author by

Admin

Updated on May 27, 2020

Comments

  • Admin
    Admin about 4 years

    There are quite a few questions on how to implement item removal inside ngRepeat directive, and as I figured out, it comes down to using ngClick and triggering some remove function passing it item's $index.

    However, I couldn't find anywhere an example where I have multiple ngRepeats:

    <div ng-controller="MyController">
        <div ng-repeat="email in user.emails">
            {{ email }} <a href>Remove</a>
        </div>
    
        <div ng-repeat="phone in user.phones">
            {{ phone }} <a href>Remove</a>
        </div>
    </div>
    

    For this, I would need to create $scope.removePhone and $scope.removeEmail which would be called using ngClick on Remove anchor. But I'm looking for a more generic solution. Especially since I have many pages with many ngRepeats .

    I was thinking about writing a directive which would be placed on Remove anchor and would do something like this:

    1. Find ngRepeat among parent elements.
    2. Read what it's iterating over ('user.emails' in first case, 'user.phones' in second)
    3. Remove $index element from THAT model.

    So the markup would look something like this:

    <div ng-controller="MyController">
        <div ng-repeat="email in user.emails">
            {{ email }} <a href remove-directive="$index">Remove</a>
        </div>
    
        <div ng-repeat="phone in user.phones">
            {{ phone }} <a href remove-directive="$index">Remove</a>
        </div>
    </div>
    

    Is what I'm looking for possible to achieve and what would be the preferred way to do this?

    Current hacky solution

    Here is how I do it currently. It's hacky and ugly but gets the job done until I figure out a prettier way.

      myAppModule.controller('MyController', function ($scope, $parse, $routeParams, User) {
        $scope.user = User.get({id: $routeParams.id});
    
        $scope.remove = function ($index, $event) {
          // TODO: Find a way to make a directive that does this. This is ugly. And probably very wrong.
          var repeatExpr = $($event.currentTarget).closest('[ng-repeat]').attr('ng-repeat');
          var modelPath  = $parse(repeatExpr.split('in')[1].replace(/^\s+|\s+$/g, ''));
    
          $scope.$eval(modelPath).splice($index, 1);
        };
      });
    

    And in DOM:

    <div ng-repeat="email in user.email" class="control-group">
      <label class="control-label">
        {{ "Email Address"|_trans }}
      </label>
    
      <div class="controls">
        <input type="text" ng-model="email.address">
    
        <span class="help-inline"><a href ng-click="remove($index, $event)">{{ "Delete"|_trans }}</a></span>
      </div>
    </div>