Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS: ng-repeat list is not updated when a model element is spliced from the model array

I have two controllers and share data between them with an app.factory function.

The first controller adds a widget in the model array (pluginsDisplayed) when a link is clicked. The widget is pushed into the array and this change is reflected into the view (that uses ng-repeat to show the array content):

<div ng-repeat="pluginD in pluginsDisplayed">     <div k2plugin pluginname="{{pluginD.name}}" pluginid="{{pluginD.id}}"></div> </div> 

The widget is built upon three directives, k2plugin, remove and resize. The remove directive adds a span to the template of the k2plugin directive. When said span is clicked, the right element into the shared array is deleted with Array.splice(). The shared array is correctly updated, but the change is not reflected in the view. However, when another element is added, after the remove, the view is refreshed correctly and the previously-deleted element is not shown.

What am I getting wrong? Could you explain me why this doesn't work? Is there a better way to do what I'm trying to do with AngularJS?

This is my index.html:

<!doctype html> <html>     <head>         <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js">         </script>         <script src="main.js"></script>     </head>     <body>         <div ng-app="livePlugins">             <div ng-controller="pluginlistctrl">                 <span>Add one of {{pluginList.length}} plugins</span>                 <li ng-repeat="plugin in pluginList">                     <span><a href="" ng-click="add()">{{plugin.name}}</a></span>                 </li>             </div>             <div ng-controller="k2ctrl">                 <div ng-repeat="pluginD in pluginsDisplayed">                     <div k2plugin pluginname="{{pluginD.name}}" pluginid="{{pluginD.id}}"></div>                 </div>             </div>         </div>     </body> </html> 

This is my main.js:

var app = angular.module ("livePlugins",[]);  app.factory('Data', function () {     return {pluginsDisplayed: []}; });  app.controller ("pluginlistctrl", function ($scope, Data) {     $scope.pluginList = [{name: "plugin1"}, {name:"plugin2"}, {name:"plugin3"}];     $scope.add = function () {         console.log ("Called add on", this.plugin.name, this.pluginList);         var newPlugin = {};         newPlugin.id = this.plugin.name + '_'  + (new Date()).getTime();         newPlugin.name = this.plugin.name;         Data.pluginsDisplayed.push (newPlugin);     } })  app.controller ("k2ctrl", function ($scope, Data) {     $scope.pluginsDisplayed = Data.pluginsDisplayed;      $scope.remove = function (element) {         console.log ("Called remove on ", this.pluginid, element);          var len = $scope.pluginsDisplayed.length;         var index = -1;          // Find the element in the array         for (var i = 0; i < len; i += 1) {             if ($scope.pluginsDisplayed[i].id === this.pluginid) {                 index = i;                 break;             }         }          // Remove the element         if (index !== -1) {             console.log ("removing the element from the array, index: ", index);             $scope.pluginsDisplayed.splice(index,1);         }      }     $scope.resize = function () {         console.log ("Called resize on ", this.pluginid);     } })  app.directive("k2plugin", function () {     return {         restrict: "A",         scope: true,         link: function (scope, elements, attrs) {             console.log ("creating plugin");              // This won't work immediately. Attribute pluginname will be undefined             // as soon as this is called.             scope.pluginname = "Loading...";             scope.pluginid = attrs.pluginid;              // Observe changes to interpolated attribute             attrs.$observe('pluginname', function(value) {                 console.log('pluginname has changed value to ' + value);                 scope.pluginname = attrs.pluginname;             });              // Observe changes to interpolated attribute             attrs.$observe('pluginid', function(value) {                 console.log('pluginid has changed value to ' + value);                 scope.pluginid = attrs.pluginid;             });         },         template: "<div>{{pluginname}} <span resize>_</span> <span remove>X</span>" +                        "<div>Plugin DIV</div>" +                   "</div>",         replace: true     }; });  app.directive("remove", function () {     return function (scope, element, attrs) {         element.bind ("mousedown", function () {             scope.remove(element);         })     };  });  app.directive("resize", function () {     return function (scope, element, attrs) {         element.bind ("mousedown", function () {             scope.resize(element);         })     }; }); 
like image 299
janesconference Avatar asked Mar 18 '13 11:03

janesconference


People also ask

What is difference between ng-repeat and Ng options?

ng-repeat creates a new scope for each iteration so will not perform as well as ng-options. For small lists, it will not matter, but larger lists should use ng-options. Apart from that, It provides lot of flexibility in specifying iterator and offers performance benefits over ng-repeat.

What is Ng-repeat in AngularJS?

AngularJS ng-repeat Directive The ng-repeat directive repeats a set of HTML, a given number of times. The set of HTML will be repeated once per item in a collection. The collection must be an array or an object. Note: Each instance of the repetition is given its own scope, which consist of the current item.

Does ng-repeat create a new scope?

The controller creates a child scope and the ng-repeat , which will create an LI element for each item in the list of To Do items. It also creates a new scope for each element.

What can I use instead of NG-repeat?

And you should consider using ng-repeat with pagination. You can consider using transclusion inside a custom directive, to achieve the behavior you are looking for without using ng-repeat.


1 Answers

Whenever you do some form of operation outside of AngularJS, such as doing an Ajax call with jQuery, or binding an event to an element like you have here you need to let AngularJS know to update itself. Here is the code change you need to do:

app.directive("remove", function () {     return function (scope, element, attrs) {         element.bind ("mousedown", function () {             scope.remove(element);             scope.$apply();         })     };  });  app.directive("resize", function () {     return function (scope, element, attrs) {         element.bind ("mousedown", function () {             scope.resize(element);             scope.$apply();         })     }; }); 

Here is the documentation on it: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply

like image 102
Mathew Berg Avatar answered Sep 22 '22 23:09

Mathew Berg