Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

$destroy event is triggered on leaving view before animation/transition ends #1643

Closed
ghoullier opened this issue Dec 18, 2014 · 14 comments · Fixed by #2346
Closed

$destroy event is triggered on leaving view before animation/transition ends #1643

ghoullier opened this issue Dec 18, 2014 · 14 comments · Fixed by #2346

Comments

@ghoullier
Copy link

When animations are associated to a view using ngAnimate, $destroy event is triggered on leaving view before animation/transition ends.

This event should be triggered after animation/transition end?

Plunker

[email protected]
[email protected]
[email protected]

@ghoullier ghoullier changed the title $destroy event is triggered on leaving view before animation/transition end $destroy event is triggered on leaving view before animation/transition ends Dec 18, 2014
@Roman-Didenko
Copy link

+1

Roman-Didenko added a commit to Roman-Didenko/ui-router that referenced this issue Mar 22, 2015
@philBrown
Copy link
Contributor

This is because this part in cleanupLastView() doesn't wait for the renderer.leave promise

if (currentScope) {
    currentScope.$destroy();
    currentScope = null;
}

@nateabele
Copy link
Contributor

Okay, so, I guess it should?

@philBrown
Copy link
Contributor

@nateabele yes, because until the animation completes, the leaving state's scope is still relevant.

For example, I have an Highcharts chart in my view (using highcharts-ng). When I transition to another state via an animation, the chart is destroyed (and removed from the DOM) before the animation completes.

@nateabele
Copy link
Contributor

Well, I'll patch it as soon as I can, unless you think you can do it faster.

@manro
Copy link

manro commented Sep 13, 2015

I think this code change would help:

Current cleanupLastView function:

function cleanupLastView() {
  if (previousEl) {
    previousEl.remove();
    previousEl = null;
  }

  if (currentScope) {
    currentScope.$destroy();
    currentScope = null;
  }

  if (currentEl) {
    renderer.leave(currentEl, function() {
      previousEl = null;
    });

    previousEl = currentEl;
    currentEl = null;
  }
}

cleanupLastView function with deferred animate-out element scope destroy:

function cleanupLastView() {
  if (currentEl) {
      (function(previousEl, currentScope) {
          renderer.leave(currentEl, function() {
              if (previousEl) {
                  previousEl.remove();
                  previousEl = null;
              }
              if (currentScope) {
                  currentScope.$destroy();
                  currentScope = null;
               }

               previousEl = null;
               currentScope = null;
          });
      })(previousEl, currentScope);
  } else {
      if (previousEl) {
          previousEl.remove();
          previousEl = null;
      }
      if (currentScope) {
          currentScope.$destroy();
          currentScope = null;
      }
  }
  previousEl = currentEl;
  currentEl = null;
}

@nateabele
Copy link
Contributor

Great. PR + unit test?

Teemu pushed a commit to fastmonkeys/ui-router that referenced this issue Oct 29, 2015
when view is animated using ngAnimate, $destroy event is triggered before
animation ends.

closes angular-ui#1643
Teemu pushed a commit to fastmonkeys/ui-router that referenced this issue Oct 30, 2015
when view is animated using ngAnimate, $destroy event is triggered before
animation ends.

closes angular-ui#1643
Teemu pushed a commit to fastmonkeys/ui-router that referenced this issue Oct 30, 2015
when view is animated using ngAnimate, $destroy event is triggered before
animation ends.

closes angular-ui#1643
Teemu pushed a commit to fastmonkeys/ui-router that referenced this issue Oct 30, 2015
when view is animated using ngAnimate, $destroy event is triggered before
animation ends.

closes angular-ui#1643
Teemu pushed a commit to fastmonkeys/ui-router that referenced this issue Oct 30, 2015
when view is animated using ngAnimate, $destroy event is triggered before
animation ends.

closes angular-ui#1643
@SmuliS
Copy link

SmuliS commented Oct 30, 2015

@nateabele Ping. Now there is a PR open with tests. This is a very welcome PR if you have state change animations!

@nateabele
Copy link
Contributor

@SmuliS Merged.

@ghoullier
Copy link
Author

👍

@petermolyneux
Copy link

This fix worked for some of our situations, but made it worse for others.

The problem for us is when we have a Kendo grid on a page with a base page. When leaving the page we get an error something like "Can't set property template of null". Before the fix we got the error only if the grid was in a template for a component with transclude. Now it works fine in that situation, but we get the same error when our routing is set up to use base pages.

I think the problem with the fix is that we now don't control the order things are removed/destroyed in.

I.e. If a scope has a child scope and the child contains animations, the parent will try to broadcast destroy before the child is ready.

I hacked the code to get it to work. Basically what I do is wait for all elements to report there leave-events and then do a cleanup for all of them at once.

The code might help you understand the issue and find a good solution:

function cleanupLastView() {
    var _currentEl;
    var i = 0;

    if (currentScope) {
        currentScope._willBeDestroyed = true;
    }

    function cleanOld(prevEl, currScope) {
        if (prevEl) {
            prevEl.remove();
        }

        if (currScope) {
            try {
                currScope.$destroy();
            } catch (e) {
                currScope.$destroy();
            }
        }
    }

    if (currentEl) {
        awaitingCleanup.push({ prevEl: previousEl, currScope: currentScope, currEl: currentEl, readyForRemoval: false });
        _currentEl = currentEl;
        renderer.leave(_currentEl, function () {
            var allReady = true;
            for (i = 0; i < awaitingCleanup.length; i++) {
                if (awaitingCleanup[i].currEl === _currentEl) {
                    awaitingCleanup[i].readyForRemoval = true;
                }
                allReady = allReady && awaitingCleanup[i].readyForRemoval;
            }
            if (allReady) {
                while (awaitingCleanup.length > 0) {
                    cleanOld(awaitingCleanup[awaitingCleanup.length - 1].prevEl, awaitingCleanup[cleanUpOrder.length - 1].currScope);
                    awaitingCleanup.pop();
                }
            }
            previousEl = null;
        });

        previousEl = currentEl;
    } else {
        cleanOld(previousEl, currentScope);
        previousEl = null;
    }

    currentEl = null;
    currentScope = null;
}

Hope this help,

Peter Molyneux

@christopherthielen
Copy link
Contributor

@petermolyneux I'd like to understand your use case better.

Please explain your setup.

  • ui-views
  • animations
  • how do you utilize $destroy

If we expose the animation promise, will you be able to clean your resources using that promise, instead of $on($destroy)?

Any chance you could set up a representative plunker, so we can accomodate your use case?

@christopherthielen
Copy link
Contributor

We will be reverting this change in favor of exposing the animation promise #2562

@dmitry-dedukhin
Copy link

#2614
Another terrible issue caused by this change.

ExpFront pushed a commit to ExpFront/ui-router that referenced this issue Jun 23, 2016
when view is animated using ngAnimate, $destroy event is triggered before
animation ends.

closes angular-ui#1643
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants