Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

ngView animation enter is not firing on page load. #15589

Closed
gogachinchaladze opened this issue Jan 10, 2017 · 20 comments
Closed

ngView animation enter is not firing on page load. #15589

gogachinchaladze opened this issue Jan 10, 2017 · 20 comments

Comments

@gogachinchaladze
Copy link

I'm using latest angular (v1.6.1), ngRoute and ngAnimate.

The thing is that ngView animation enter is not firing when you go to website (when the website loads first time).

Here is my code:

var app = angular.module('app', ['ngAnimate', 'ngRoute']);

app.animation('.view', function () {
  return {
    enter : function (element, done) {
      alert('enter');
      done();
    },
    leave : function (element, done) {
      alert('leave');
      done();
    }
  }
});

//Config

app.config(function ($routeProvider, $locationProvider, $httpProvider){

  $routeProvider.when('/test.php',{
    template : function () {
      return "<div>Welcome to test page</div>";
    }
  });
  $routeProvider.when('/test.php/:path',{
    template : function () {
      return "<div>Inside page</div>";
    }
  });

  $locationProvider.html5Mode({enabled: true, requireBase: true});
});

I've seen this issue here: #10536

That says that "it's a feature, not a bug". But the thing is that on production website, it sometimes fires and sometimes not (especially in safari and mobile browsers).

For example, when I make the template load slower:

$routeProvider.when('/test.php',{
    template : function () {
      return "<div>Welcome to test page</div>";
    },
    resolve: {
      message: function ($timeout, $q) {
        console.log('asdasa')
        return $q(function(resolve){
          $timeout(function () {
            resolve('message');
          },1000);
        });
      }
    }
  });
  $routeProvider.when('/test.php/:path',{
    template : function () {
      return "<div>Inside page</div>";
    },
    resolve: {
      message: function ($timeout, $q) {
        return $q(function(resolve){
          $timeout(function () {
            resolve('message');
          },1000);
        });
      }
    }
  });

Or with PHP:

sleep(1)

the enter event fires, but maybe when it is in the cache or when browser gets it quickly, it does not.

I tried several dirty hacks mentioned here but none of them works:

For example:

$rootElement.data("$$ngAnimateState").running = false;

Also this:

app.run(function($animate) {
  $animate.enabled(true);
});

But it has a very strange behavior and in safari it doesn't help.

Thanks in advance and I'll be waiting for your help!

@gkalpak
Copy link
Member

gkalpak commented Jan 10, 2017

@gogachinchaladze, can you create runnable demo that reproduced the problem (preferrably using CodePen, plnkr or a similar service)?

But it has a very strange behavior and in safari it doesn't help.

Also clarify what "strange behavior" means.

@gogachinchaladze
Copy link
Author

@gkalpak

http://jsbin.com/yifunodota/edit?html,js,output

Here is the fiddle I'm talking about.

I'll examine the behavior deeper and write it too :)

@gkalpak
Copy link
Member

gkalpak commented Jan 10, 2017

I don't see something unexpected tbh. [As documented] animations are disabled upon bootstrap (and wait for templates to be downloaded and 1.5 digests before getting enabled).

When the template is readily available during app bootstrap, animations are still disabled, so they don't run for the initial ngView enter. If you somehow delay the first ngView load (e.g. by having an asynchronous resolve), then animations are probably enabled by the time the ngView enters for the first time. Using $animate.enabled(true), makes the ngView animate in all cases.

This is what I observed (and is expected behavior). Are you seeing something different? Can you detail the steps to reproduce the issue?

@gogachinchaladze
Copy link
Author

Yes, you are right.

Just one more thing:

When I go to the page first time, $animate.enabled(true) doesn't work on safari. But when I reload the page being on the same tab, it does.

Sorry I cannot create JsBin for this, as it's so browser specific.

@gkalpak
Copy link
Member

gkalpak commented Jan 11, 2017

What version of Safari? I tried your jsBin on Safari 10 yesterday and it worked as expected (I think).

@gogachinchaladze
Copy link
Author

Can you please see the updated jsBin ?

The problem is that when I enable animation on view element only, the enter event doesn't fire, but when I enable animation on whole page, it does.

While debugging, I even found out that animation on view element is enabled on bootstrap, but the whole page animation is disabled, so I'm not surprised that enabling only view element animation doesn't help.

But enabling animation on whole page is not the thing I want to do.

@gkalpak
Copy link
Member

gkalpak commented Jan 11, 2017

When animations are globally disabled (via $animate.enabled(false)) there is no way to enable them for a specific element.

If you want to only enable animations on a specific element, you can generally use something like:

$animate.enabled(document.body, false);
$animate.enabled(mySpecificElem, true);

But in this case, you can't enable animations on the [ng-view] element, because you can't get hold of the newly created node before $animate does. As a work-around (depending on your usecase), you can either use some combination of classes along with $animateProvider.classNameFilter() or put the [ng-view] inside a container and enable animations on that container only; i.e.:

<body>
  <div class="view-container">
    <div class="view" ng-view></div>
  </div>
</body>
app.run(function ($animate) {
  $animate.enabled(true);
  $animate.enabled(document.body, false);
  $animate.enabled(document.querySelector('.view-container'), true);
})

#14914 (once/if merged) will provide another way to handle this.

@gogachinchaladze
Copy link
Author

Ok, got it. Done everything and works great in JSBIN

But on real website it has the same problem. When I open a new tab and go to website, the animation enter still does not fire. I even created a simple index.html, without any excess code, and still the same problem occurred.

@gkalpak
Copy link
Member

gkalpak commented Jan 12, 2017

😞 - No idea what might be causing the different behavior, sorry. Happy to look into it if you can provide a reproduction.

In the meantime, I am going to close this, since everything seems to work as expected (at least on jsBin 😛). Feel free to continue the discussion below.

@gkalpak gkalpak closed this as completed Jan 12, 2017
@gogachinchaladze
Copy link
Author

gogachinchaladze commented Jan 12, 2017

@gkalpak

Here is a link where you can test it: http://goga.lsapps.me/test.php

In chrome it works well, but when I test it in safari, on first page load it doesn't fire, but if I reload on same tab, it does.

on http://goga.lsapps.me/en

you can see it on real website. Again, works well on chrome, but same problem on safari.

Thank you so much for help!

@gkalpak
Copy link
Member

gkalpak commented Jan 12, 2017

Have you tried replacing alert(...) with console.log(...).

@gogachinchaladze
Copy link
Author

Yes I did, same problem :(

@gkalpak
Copy link
Member

gkalpak commented Jan 12, 2017

On Chrome for example, it isn't run when the tab is not focused. This seems something browser-related.
I'll try to take a look in Safari.

@gogachinchaladze
Copy link
Author

Yes, I noticed that too.

@gogachinchaladze
Copy link
Author

@gkalpak

Nothing new, yes?

@gkalpak
Copy link
Member

gkalpak commented Jan 13, 2017

So, the difference is that in Safari document.hidden is true when first loading a page. And when document is hidden we skip animations. In Chrome document.hidden is true when you open on a new (unfocused) tab, that's why the animation is skipped.

For whatever reason, Safari sets document.hidden to false, when you first load the page (but not when you reload), thus causing $animate to skip animations.

@gogachinchaladze
Copy link
Author

Is there any way to always animate not depending on document.hidden?

@gkalpak
Copy link
Member

gkalpak commented Jan 13, 2017

It is not directly supported (as part of the public API). The obvious hack outside Angular is to set document.hidden to true (but don't try this at home).

We are using a private service to track the visibility of the document, so you can overwrite that; it's called $$isDocumentHidden and it is a function returning a boolean. $animate calls it before every animation to determine if it should run or not. You could overwrite it to always return true (but note that there might be issues with the animations when the document is hidden - for example some browsers don't flush animation frames as often etc):

app.value('$$isDocumentHidden', function() { return true; });

@gogachinchaladze
Copy link
Author

Yay! Finally, works well!

Just one thing to note:
$$isDocumentHidden needs to return false, not true:

app.value('$$isDocumentHidden', function() { return false; });

Hope nothing goes wrong with this hack!

@gkalpak
Thank you so much for help!

@gkalpak
Copy link
Member

gkalpak commented Jan 13, 2017

$$isDocumentHidden needs to return false, not true:

Doh!

Glad it worked for you and that everything works as expected inside $animate. I forgot to mention (in case it is not clear), that $$isDocumentHidden is a private, undocumented API. You are free to use it, but you are accepting the risk that it might change, break or be removed any time without notice 😁

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants