How can I make the click event inside the ng-repeat only re-render the affected element (i.e. not run $ apply)?

My example code (below) has two problems:

  • the "renderthis" function is called twice on an element on simple page load

  • The main problem is when the button is clicked, I am assuming that after eval'ing the doit () expressions in the scope, ng-click calls $ applications are applied, which results in two DOM elements for each element being overwritten, rendered .. now I understand that the scope for each item here is somehow tied to the parent scope, or tied to it by some weird isolation or transexclusion or something I still have to master .. is there a way to make ng -click only call $ digest in content areas for each element or something like that?

here is my code:

HTML:

<body ng-controller="MainCtrl">
     <ul>
        <li ng-repeat="item in items">
        <div>
         <span>{{item.title}}</span> 
         <span>{{renderthis()}}</span>
         <span>{{item.number}}</span>
         <button ng-click="doit()">CLIQUE!</button>
        </div>
        </li>
      </ul>
  </body>

      

JS:

var app = angular.module('angularjs-starter', []);

app.controller('MainCtrl', function($scope) {
  $scope.doit = function(){
    console.log('doing it for item with title --> ', this.item.title);
  }
  $scope.renderthis = function(){
    console.log('rendering this for item with title -->', this.item.title);
    return '|';
  }
  $scope.items = [{title: 'hello', number: 1}, {title: 'shalom', number: 42}];
});

      

Or check out this plnkr:

http://plnkr.co/edit/VkR82lbJ0Mi2z12RqBfW

+3


source to share


3 answers


After some ping-pong with @Mark regarding this (see below) it was suggested to rephrase the question to something like this: "How can I make a click event inside an ng-repeat scope only re-accept the affected element (i.e. not fire $ apply) " . This is to emphasize, as Mark explained, that there isn't much to get around by using "ng-click" as it will hard-coded "$ apply" and that starts the digest loop and so on. (see below)

I'll settle for my own answer, which includes my own version of ng-click that uses $ digest instead of $ apply and therefore does not cause all ng-repeat'ed elements to be re-rendered.

Here is the edited ng-click (xng-click) code:



var xngEventDirectives = {};

angular.forEach(
  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup'.split(' '),
  function(name) {
    var directiveName = directiveNormalize('xng-' + name);
    xngEventDirectives[directiveName] = ['$parse', function($parse) {
      return function(scope, element, attr) {

        var fn = $parse(attr[directiveName]);
        element.bind(name.toLowerCase(), function(event) {
        fn(scope, {$event:event});
        scope.$digest();
        });
      };
    }];
  }
);

      

And here is plnkr for demonstration:

http://plnkr.co/edit/3i7TCEkBtxCFw2ESeyaU .

+3


source


Is there a way to make ng-click only call $ digest in the child area for each element or something?

Not. To achieve this functionality, you need to create your own directive.

ng-click starts the $ digest loop (see Runtime section), which will (among other things) check all clocks. Each set of {{}} s in the view sets $ watch. This is why you see log () s from renderthis function when pressed.

I don't think anything has been re-rendered unless the clock detects a change.



Regarding "twice called on page load":

(Since watchExpression can execute multiple times per $ digest cycle when a change is detected, be prepared for multiple calls to your listener.) - Scope # $ watch API doc

@Artem has a nice explanation of the Angular digest loop and dirty file checking . To summarize, because Angular found something that changed in the first $ digest loop, it runs another one to make sure all models are stabilized (not changed).

+2


source


You can try ngp-local-click instead of ng-click -> https://github.com/ansukla/ng-perf

0


source







All Articles