angular-ui / ui-router

The de-facto solution to flexible routing with nested views in AngularJS
http://ui-router.github.io/
MIT License
13.54k stars 3k forks source link

[1.0] Avoiding multiple ui-view elements #3545

Closed adamreisnz closed 6 years ago

adamreisnz commented 7 years ago

I have encountered a stubborn situation when transitioning between two routes the content on my page was pushed 80 pixels down momentarily just before the content of the new page was shown.

Upon lengthy analysis, I discovered that this is due to ui-router inserting a second ui-view block in my code which contains the new route, prior to removing the old one.

The presence two of these content blocks caused the quick but visible and very annoying jump of the page.

So with a template like:

<main class="AppContents" ui-view></main>

Ui-router will turn this into the following during a transition:

<main class="AppContents" ui-view><!-- new route contents --></main>
<main class="AppContents" ui-view><!-- old route contents --></main>

I understand that this might be useful when wanting animations between two states, but is there a way to disable this and to only ever have one ui-view block present, so that it won't mess up my page layout when transitioning?

It's slightly concerning that so much time (relatively) passes between inserting the new block and removing the old one, for the jump to actually be discernible. I would have imagined that these two operations happen almost instantaneously.

adamreisnz commented 7 years ago

Upon further analysis I found that this problem may have been caused by using async functions for my onBefore transition hooks. That is a bit strange, as I would expect them to work just the same as with returning a promise. But reverting back to explicitly returning a promise resolved the jumping issue.

Any thoughts why this could be an issue? E.g.

$transitions.onBefore({}, transition => {
  return $store.user
    .get()
    .then(user => {
      //do stuff with user
    });
});

vs

$transitions.onBefore({}, async transition => {
  const user = await $store.user.get();
  //do stuff with user
});
christopherthielen commented 6 years ago

Any thoughts why this could be an issue? E.g.

No, sorry... I have no idea why this would change the behavior. I'm suspicious of your conclusion that changing from returning a promise to async function changes the behavior (it shouldn't);

The two elements are indeed used for animation support. You might be able to disable animation globally, or for a specific element to avoid this behavior.

It's slightly concerning that so much time (relatively) passes between inserting the new block and removing the old one, for the jump to actually be discernible. I would have imagined that these two operations happen almost instantaneously.

Agreed. If there is no animation matching for a specific ui-view, they should not be visible at the same time. Does your app use animations? Could one of them be matching this ui-view? Can you reproduce this in a plunker? A basic test doesn't show the same problem.

adamreisnz commented 6 years ago

No, the app doesn't use animation on ui-view, I don't animate the view ports at all.

I remember digging into this a lot, because it "suddenly" started happening. But once I removed the async function calls and converted back to promises, the problem disappeared again.

As you can imagine it will be pretty hard if not impossible to reproduce this in an isolated case, because it could have to do maybe with how the build process transpiles the async functions to ES5.

If I have more time I can try to dig into it some more.

adamreisnz commented 6 years ago

This one most certainly is still an issue. I've just checked again, and introduced async and await in my onBefore hooks instead of returning a promise, and it's causing the UI to jump and shows two elements at the same time. Will try it out in a plunkr and see if it can be reproduced at all.

adamreisnz commented 6 years ago

This plunkr is the closest I can get to demonstrating the issue, but it's only because I've added a transition to the css of the ui-view block. Remove that and the issue does not occur. But in essence, this behaviour is exactly what I'm seeing in our production app the moment I use async/await instead of returning promises. Changing that around in the plunkr doesn't have an effect.

http://plnkr.co/edit/wlDPpd1Oc1YE7I3BTcis?p=preview

Try switching between home and foo routes. Due to the animation, ngAnimate will duplicate the ui-view elements and insert two of them side by side momentarily. However, this only happens if you apply transitions in your CSS on the elements. We don't have any transitions or styling applied to our ui-view elements other than this one entry:

image

There's not other transitions or animations applied to any of the elements or classes that are involved.

Maybe the plunkr app test app is too light weight to be causing issues, or maybe it's because plunkr doesn't transpile the async/await code into something else, there's too many variables at this point to draw a definite conclusion. It could be an ngAnimate issue as well.

I'd be happy to screenshare and show you the behaviour once I change from promises to async, but I don't know if we'll be able to get to the bottom of it.

Feel free to close the issue if you think there's nothing to do about it at this stage.

christopherthielen commented 6 years ago

Closing until we can reproduce this without an animation on the ui-view