ng-view along with ng-animate executes directive twice
Asked Answered
C

2

9

while i was playing with ng-view and ng-animate from angularjs 1.1.4, i noticed that directives are executed twice. once for the element in the view that enters the screen and again for the element in the view which leaves the screen (while the directive was already executed for the element when the view entered the screen).

for my understanding, directives should only be executed for elements which enter the screen and not for the ones which leaves. or am i missing something?

<div ng-app="app">
    <div ng-controller="AppCtrl">
        <div class="btn-group">
            <a class="btn btn-primary" href="./">foo</a>
            <a class="btn btn-primary" href="bar/">bar</a>
        </div>
        <span class="btn btn-success">{{count}}</span>
        <div class="view" ng-view ng-animate="{enter: 'view-enter', leave: 'view-leave'}"></div>
    </div>
</div>
var app = angular.module('app', []);

app.config(function ($routeProvider, $locationProvider) {
    $routeProvider
        .when('/', {
            template: '<h1 color>foo</h1>'
        })
        .when('/bar', {
            template: '<h1 color>bar</h1>'
        })
        .otherwise({
            redirectTo: '/'
        });

    $locationProvider.html5Mode(true);
});

app.controller('AppCtrl', function ($scope) {
    $scope.count = 0;
    $scope.incrementCount = function () {
        $scope.count++;
    }
});

app.directive('color', function () {
    return {
        restrict: 'A',
        link: function ($scope, $element, $attrs) {
            // executed once the app inits but
            // twice when you change the view
            $scope.incrementCount();
        }
    }
});

i've setup a jsfiddle for this http://jsfiddle.net/mediastuttgart/f4FPj/1/

as you can see, the counter says 1 when the app inits. but if you start navigating, the counter increments by 2.

cheers michael

EDIT

of course a workaround could be to add a class to the element and check for it in the directive – but i guess that's not the way it should be accomplished.

link: function ($scope, $element, $attrs) {
    if ($element.hasClass('processed')) return;
    $element.addClass('processed');
    $scope.incrementCount();
}

http://jsfiddle.net/mediastuttgart/f4FPj/2/

Cultivable answered 23/4, 2013 at 5:29 Comment(2)
I've not read the following anywhere, "directives should only be executed for elements which enter the screen and not for the ones which leaves." Do you have a reference for that?Resor
I haven't read it, but i would assume it. for the case a directive should be executed on a "stage leaving" element again, this could be a special attribute along the element which tells angular to run the code again (or maybe only once when the element is leaving the stage). for eg. a message like "heya, shutting down section xyz" which is generated by the directive when the element is leaving the stage.Cultivable
V
2

It's going to fire the directive for each event that directive handles. See this fiddle, if you remove the leave event, it only fires once.

<div class="view" ng-view ng-animate="{enter: 'view-enter'}"></div>

http://jsfiddle.net/WmSP8/1/

Notice the documentation says "The event1 and event2 attributes refer to the animation events specific to the directive that has been assigned."(http://code.angularjs.org/1.1.4/docs/api/ng.directive:ngAnimate)

It's essentially saying that you can build directives with custom events not present in all directives, and the animator service will allow your directive to know which animations to run when these events fire in your directive. However this means your directive can possibly execute for each event handled in your ng-animate attribute. So when you configure both the enter and leave, the directive executes for them both.

I haven't actually written any code to test this, this is just my interpretation of the documentation, and the jsfiddle seems to confirm the general premise.

Vancouver answered 3/5, 2013 at 12:27 Comment(2)
while this makes sense, i still can't figure out the overall logic of this. if you have a directive which should change the color of an element to red on "view-enter", why should this code be executed again, when the element leaves the view – as the element is already colored red. it would make sense if you could distinguish inside the directive between enter and leave. hence you could change the color to red on "view-enter" and for eg to green on "view-leave".Cultivable
to stay with my current app, i have a dropdown directive which gets hidden by the linkFn with $element.hide(). when the user clicks on a button, the menu slides down. now if the user clicks on a link inside the menu, i have no options to slideup the menu, because the directive runs again and instantly executes $element.hide().Cultivable
C
0

The issue seems to be gone with angular 1.1.5. Guess this commit has fixed it ngView: accidentally compiling leaving content (9956baed) https://github.com/angular/angular.js/commit/9956baedd73d5e8d0edd04c9eed368bd3988444b

Cultivable answered 25/6, 2013 at 5:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.