Closed UserGalileo closed 1 year ago
I asked myself if it could be possible to make the client-app bootstrap in a hidden div and wait for it to be rendered before destroying the static HTML, but I didn't manage to get it right and I'm unsure if it would solve the problem.
This is essentially what preboot does (when it works).
I'm assuming you've also tried enabling initialNavigation in your RouterModule
?
Beyond that you likely need a creative approach, and a good place to find that is on Gitter or StackOverflow.
The problem here is that this is a known quirk of Universal right now, as we wait for hydration in the post-Ivy world. But it's not exactly an "issue" since there are known methods for overcoming this, even with apps of complex sizes and shapes. I wish I could offer more, but again this is not exactly the best place for support of non-issues and non-feature requests.
@CaerusKaru Thank you for your reply!
I'm assuming you've also tried enabling initialNavigation in your RouterModule?
Yes I've already tried, but I haven't tried it in combination with Preboot, thank you for reminding me. I assumed it was a useful solution for CSS purposes, I didn't think it would have an impact with Preboot, is this the case?
I'm not using StackOverflow or other platforms because first of all I think having the opinion of members/collaborators directly would have been better (and faster) since this is maybe THE main "issue" a lot of people have with Universal (from my point of view, obviously). I'm a consultant and this is a recurring topic in every team I've seen which uses Universal. You said that:
it's not exactly an "issue" since there are known methods for overcoming this, even with apps of complex sizes and shapes
What are you referring to? Which known methods are you talking about? Don't you think that since this is a quirk of Universal we could be more clear about what these methods are? I think it would really really help a lot of people :)
We've made big steps forward with developer experience in many points, also the official Angular documentation page about Universal was improved, but I feel that the docs are missing something, some guidance, some "Ok, you learned how these packages work, now these are your options if you're having these problems", something like the the "gotchas" that we already have a page for. It's not a "StackOverflow type of guidance" I'm talking about, I don't know if I was clear about this slight difference. It's more about best practices and real-world recurring problems and scenarios.
Just as an example, a lot of people think that now that we have TransferHttpCacheModule
we don't need to use TransferState
anymore, but it's absolutely not true because it's not an alternative and it solves just one particular problem: repeated http calls. Again I'm not saying docs are wrong, just that maybe they miss that common thread that would help more. Another example: personally it wasn't easy for me to understand how I could use Preboot in combination with Universal, thankfully I found that package mentioned in some issues and that made me realize that the package was linked in your README with a one-liner. It was not clear to me that Preboot is practically necessary in a real-world scenario if you want to overcome some of the limitations/quirks of Universal.
I hope I haven't bored you with this wall of text! What do you think about it? Could we improve the docs and make more clear which are the best practices and which are the "known methods" for overcoming these issues? I'd be glad to help too, if you need it.
PS. Also, I forgot about lazy routes: could it be that using lazy loading causes the blank page to last longer? If this is the case, is there a way to make Angular load the lazy module and THEN rebuild the DOM? Or maybe this is already happening and my eyes are wrong? :)
setting initialNavigation to 'enabled' basically does what you described towards the end - Wait for lazy loaded route to be ready before swapping in the client rendered DOM so as to avoid a flicker due to empty router-outlet in the lazy loaded route.
@vikerman Thanks, I'll try again hoping something chages, I'm sure I've tried it not so long ago in the same project and nothing seemed to change, I'll pay more attention this time :)
What do you think about making a little effort in order to have a better documentation for things like these ones? Again, "initialNavigation" is one of those things that you find only through issues, even the official Angular docs only say "Disables the initial navigation", nothing more.
I'd be really glad to help, It would be great if we could gather some people and have some sort of brainstorming to identify common problems and solutions :)
Same issue here.
Everybody is talking about initialNavigation
, StateTransfer
and TransferHttpCacheModule
.
These things do hide the problem @UserGalileo talks about in some cases, but they don't solve it.
The problem is almost unnoticeable on pages with ~500
DOM elements - it takes 100-200 milliseconds for Angular to re-render them.
But assume we have ~1.2-1.5k
DOM elements on our page (a list of products or posts with votes, tags, location, author name, price, etc). I'm working on Asus ROG G750 and still waiting 1.5-2s
last dom element to be replaced.
But it's not exactly an "issue" since there are known methods for overcoming this, even with apps of complex sizes and shapes.
@CaerusKaru, could you provide some links or keywords to these methods, please?
Here are some related issues I've been reading the last two days:
P.s. I use initialNavigation: 'enabled'
+ TransferHttpCacheModule
+ a bunch of lazy loaded modules - it works best for me, but I still can't eliminate the problem @UserGalileo talks about.
So I tried to use @angular/preboot
with enabled initialNavigation
and it works better now, at least I can't see DOM re-rendering.
Here is a commit I made to fix flickering using preboot
: https://github.com/timadevelop/saasWebClient/commit/efb38aeb152971af94db1d537e77fef7d9e40fa6
Try it out and give me some feedback, please.
The only issue I have now is a mobile view: I use a simple service in my angular app for detecting if a target device is a desktop or mobile (using it to remove desktop-specific DOM nodes on mobile and vice-versa). And unfortunately, my service re-evaluates target device only after 1 or 2 seconds after the first view was received from SSR.
// app.routing
imports: [RouterModule.forRoot(routes, {
// ...
initialNavigation: 'enabled'
})],
// app.module
imports: [
// ...
PrebootModule.withConfig({ appRoot: 'app-root' })
],
// ...
providers: [
{
provide: APP_INITIALIZER,
useFactory: function(document: HTMLDocument, platformId: Object): Function {
return () => {
if (isPlatformBrowser(platformId)) {
const dom = ɵgetDOM();
const styles: any[] = Array.prototype.slice.apply(dom.querySelectorAll(document, `style[ng-transition]`));
styles.forEach(el => {
// Remove ng-transition attribute to prevent Angular appInitializerFactory
// to remove server styles before preboot complete
el.removeAttribute('ng-transition');
});
document.addEventListener('PrebootComplete', () => {
// After preboot complete, remove the server scripts
setTimeout(() => styles.forEach(el => dom.remove(el)));
});
}
};
},
deps: [DOCUMENT, PLATFORM_ID],
multi: true
},
I tried with initialNavigation: 'enabled'
with Preboot and it seems like this solved the problem (or part of it). I still see a bit of a flicker but it's far less noticeable than before.
The problem I have with initialNavigation, is that now, since the initial navigation is performed before the main component is bootstrapped, a lot of my router-related observables (ex. Router.events
) stopped working because they miss the first navigation event, so I had to remove router observables from my services and in my components I had to use ActivatedRoute.url
which appears to be a cold observable.
Still, I need some testing to see if this behavior is acceptable and I'd leave this issue open for my previous requests regarding the docs. I hope to receive some feedback by members/collaborators.
We are adding initialNavigation: 'enabled'
as part of the express-engine schematics, since that seems like the sane default that will avoid the basic cause for flicker in many cases. We will document it as part of that.
Issue #1200
@vikerman adding initialNavigation: 'enabled'
will may works for smaller projects. But as @UserGalileo said observables on RouterEvents will stop to work as expected.
For example using https://github.com/gilsdav/ngx-translate-router will not work and should not redirect to right language route.
I think it can't be the right solution for getting a smoother transition. Right?
Hi Guys, any idea why initialNavigation: 'enabled'
is firing this warning?
ReferenceError: System is not defined
after that lazyLoad route is taking forever and never loads the page.
I'm experiencing the same issue here, lazyLoad modules are not rendering page source code instead of that I see the
What do you think about making a little effort in order to have a better documentation for things like these ones? Again, "initialNavigation" is one of those things that you find only through issues, even the official Angular docs only say "Disables the initial navigation", nothing more.
@UserGalileo https://github.com/angular/angular/issues/32571 was opened to report this just a couple weeks ago. In response to that, PR https://github.com/angular/angular/pull/32707 adds the InitialNavigation
API to the docs.
In addition, in PR https://github.com/angular/angular/pull/32702 @jbogarthyde updated the initialNavigation
docs.
@BruneXX that sounds like a related, but different issue, can you please open a new issue with a reproduction?
As I mentioned in https://github.com/angular/universal/issues/1200#issuecomment-534311296, it seems like the SSR Guide needs to be updated to include information and examples for both Preboot and InitialNavigation: 'enabled'
.
However, after reading this and the other related issues, it's not clear that the best practices or recommendations have fully solidified here. Does anyone know of any blog posts that dive deeper into this and provide some recommendations?
The Preboot library has a similar issue and a reproduction that shows that both Preboot and initalNavigation: 'enabled'
are not sufficient to solve this: https://github.com/angular/preboot/issues/75
Unfortunately, although initialNavigation
solved some of the problems, it brought other bugs in my app and it's quite frustrating.
As I said, router-based observables behave differently. Router-based libraries don't work. Guards run in a different order (ex. I am authenticating or checking for tokens in AppComponent but guards don't wait for AppComponent to run), so I don't think we can say that this is the solution to all the problems.
Honestly, after weeks, this experience is really driving me crazy... I don't want to sound polemic, this is just my experience.
I don't know @Splaktar , what could we do? I think a solution should be found internally in order to make things behave like initialNavigation: 'enabled'
but without messing with the rest of the app. Updating the docs would obviously be fine, but what if we can avoid this practice which leads to other problems?
@giacomo Hello, I'm use ngx-translate-router too, Have you solved this problem?
About this flickering, my approach for now is to put a huge page-size loading overlay until components are rendered correctly on client side, - only benefit here is that the page is still crawlable by no-javascript SEO bots. I hope we can have a better solution for this in the future so we can also benefit of a smoother page load.
I've got a smooth transition, made with prerendering and preboot combination. Github.com/rickvandermey/angular-starterkit
I use SSR to create static html files for serving and preboot comes in for the transition
@UserGalileo - We currently don't understand the issue to provide a solution. Is there a way you can create a smaller repro of the issue and share a test repo? Thanks!
@zjcboy no there is for me no solution at this point - as I said observables on RouterEvents will stop to work as expected
.
@giacomo - Is it possible to share a small repro of the issue?
@vikerman What would the test repo be like? What do you want to see that you cannot understand now? As I and others said, router observables will stop working: simply try using initialNavigation: 'enabled'
and listen for Router.events
in AppComponent: the first navigation is missed. And for the "flickering" thing that initialNavigation
partially solves, since this is one of the many issues regarding this problem, I think there is no need for further explanation, as you said in your first reply in this thread, this is how Universal works :)
I have different experiences. I have a route animation,when I remove it, the flicker disappears
I have an introduction animation in my app, so when the app is being bootstrapped, the animation reoccurs. Is there a way to prevent it? For now, I added a loading spinner until the app is bootstrapped, but that's not a good solution.
So I've created a "repro repository" with angular 9. But it seems that the problem doesn't affect angular 9. Could some one look in my code or tell me if the problem still exists. @UserGalileo can you confirm?
REPRO Link: https://github.com/giacomo/angular-initial-navigation-repro
Since the https://github.com/gilsdav/ngx-translate-router isn't fully compatible to angular 9 at the moment I can't install it.
dev - default
> NavigationStart
> Angular is running in the development mode. Call enableProdMode() to enable the production mode.
> RoutesRecognized
> ==========> redirect to new route
> NavigationCancel
> NavigationStart
> RouteConfigLoadStart
> RouteConfigLoadEnd
> RouteConfigLoadStart
> RouteConfigLoadEnd
> RoutesRecognized
> GuardsCheckStart
> ChildActivationStart
> ActivationStart
> ChildActivationStart
> ActivationStart
> ChildActivationStart
> ActivationStart
> GuardsCheckEnd
> ResolveStart
> ResolveEnd
> ActivationEnd
> ChildActivationEnd
> ActivationEnd
> ChildActivationEnd
> ActivationEnd
> ChildActivationEnd
> NavigationEnd
> Scroll
[WDS] Live Reloading enabled.
dev - initialNavigation: 'enable'
> NavigationStart
> RoutesRecognized
> ==========> redirect to new route
> NavigationCancel
> NavigationStart
> RouteConfigLoadStart
> RouteConfigLoadEnd
> RouteConfigLoadStart
> RouteConfigLoadEnd
> RoutesRecognized
> GuardsCheckStart
> ChildActivationStart
> ActivationStart
> ChildActivationStart
> ActivationStart
> ChildActivationStart
> ActivationStart
> GuardsCheckEnd
> ResolveStart
> ResolveEnd
> ActivationEnd
> ChildActivationEnd
> ActivationEnd
> ChildActivationEnd
> ActivationEnd
> ChildActivationEnd
> NavigationEnd
> Scroll
> Angular is running in the development mode. Call enableProdMode() to enable the production mode.
> [WDS] Live Reloading enabled.
prod - initialNavigation: 'enable'
> ft
> yt
> ==========> redirect to new route
> gt
> ft
> Ct
> St
> Ct
> St
> yt
> bt
> xt
> Ot
> xt
> Ot
> xt
> Ot
> vt
> wt
> _t
> Tt
> Et
> Tt
> Et
> Tt
> Et
> pt
> jt
Using:
Angular CLI: 9.0.2
Node: 10.16.0
OS: win32 x64
Angular: 9.0.1
... animations, common, compiler, compiler-cli, core, forms
... language-service, localize, platform-browser
... platform-browser-dynamic, platform-server, router
Ivy Workspace: Yes
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.900.2
@angular-devkit/build-angular 0.900.2
@angular-devkit/build-optimizer 0.900.2
@angular-devkit/build-webpack 0.900.2
@angular-devkit/core 9.0.2
@angular-devkit/schematics 9.0.2
@angular/cli 9.0.2
@ngtools/webpack 9.0.2
@nguniversal/builders 9.0.0
@nguniversal/common 9.0.0
@nguniversal/express-engine 9.0.0
@schematics/angular 9.0.2
@schematics/update 0.900.2
rxjs 6.5.4
typescript 3.7.5
webpack 4.41.2
I can conform it's still happening in Angular v9 - tested the flickering using Flex Layout
It's happening in my Angular 9 project as well. Using Angular Material.
@nicholas-davis could you please create a branch to my repo so that the angular team could reproduce it. Thanks. https://github.com/giacomo/angular-initial-navigation-repro
It's happening with Angular 10 and Angular Universal 10 too, I don't have lazy loaded modules hence initialNavigation
was of no help to me.
Still have this issue with angular9
@vikerman initialNavigation:enabled
stops the flickering for me, but the navigation in Can activate guard happens after the bootstrap finishes in the result of this: it loads the home page (not guarded) and after that (guarded page).
Expected behavior: It should have rendered the can activate navigated page from the server itself
my all routes are lazy-loaded.
@puneetverma05 this is probably the guard returns an boolean promise or observable boolean. Return an URLTREE should avoid the homepage
@rickvandermey apparently, The issue was related to the ngrx select
This issue still exist, in my case I have a landing page when first visit, landing page is visible for about 100ms, it is really bad ux for first time visitors of the app.
Already tried solutions like Transferstate but in my case does not meet my expectations to solve the issue. My landing page do not have any XHR request.
I also tried the TransferHttpCacheModule, I notice that first visit, sometimes it loads correctly without the glimpse of landing page, and sometimes it lessens the flicker amount of time for about 20ms but still I can see a glimpse of my landing page, after that it somehow fix my issue, but when I clear my cache, it returns back to previous state which the landing page is visible for about 20ms.
Also tried Preboot as what others is suggesting, but it is not suitable for my app, I have a mat-progress-spinner which acts as loading, when preboot is enabled and I hit F5/refresh my loading icon freezes for a while, it does not animate, which I think is very ugly and after about 1sec it goes back to normal state (app is working as usual like normal angular app).
I'm actually stuck here and I can't just leave it as it is.
This problem still perists in Angular 13. I created a repro: https://github.com/webberig/universal-rehydration-problem
The steps to create this repo were:
lazy.component.ts
changed, added debugger;
in the constructor to pause browser when component is createdmain.ts
don't execute bootstrap()
but expose it to the window object globally.To reproduce the problem, please follow these steps:
yarn dev:ssr
You will see the server-side rendered version. Angular is not loaded because of step 5 above. The content of the lazy component is present in the DOM as expected:
bootstrap()
in the dev tools consoleYou'll notice the dev tools has paused execution in the lazy.component. The page will be blank at this point. You'll see that that the app-lazy
element has been removed:
The fact that the server-side rendered content of the router-outlet is removed while the lazy.component
's constructor is still being executed proves to be problematic. The severity of flickering may increase depending on what happens on the page. In my application, the blank page is very noticeable and annoying.
My last attempt to solve this was to detach change detection, as I figured the component was being rendered in a blank state before data was read. Since the old HTML is already gone when the constructor is executed, tweaking the change detection is no use. I would expect the old HTML to be replaced only when the new component renders the first time (ie. after init lifecycle hook)
@webberig did you try initialNavigation: 'enabledBlocking',
I tried on angular 13 and it working good.
@webberig did you try
initialNavigation: 'enabledBlocking',
I tried on angular 13 and it working good.
Yes, this is already added automatically when you add universal. https://github.com/webberig/universal-rehydration-problem/blob/main/src/app/app-routing.module.ts
If you add a breakpoint as described in my previous comment, you will see that the server-side html is gone.
Your app may just be small and fast enough to not notice a flicker, but it's there.
I got much improvement after adding preboot, though.
This should be fixed with the current hydration efforts. Let's keep tracking such issues in https://github.com/angular/angular/issues/23427
This issue has been automatically locked due to inactivity. Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.
Hi everybody!
I've spent some hours on this platform hoping to find a solution to my main problem with Universal (and SSR in general), I've read a lot of issues but I just can't find a solution. This post may seem a duplicate of many others, but I want to take a different approach: I'd like to ask some questions, hoping to get some useful info from this issue, and hoping it won't be closed and marked as duplicate because it's not my intention to duplicate issues. If what I describe here is the desired behavior of Universal or it's something that you're dealing with right now, perfect, I'm not trying to change things or report bugs.
From what I know:
This behavior causes a slight flicker, because the whole body is discarded (leading to a blank page) and re-rendered (leading to the "old" server-generated page, which is in reality the client app). There are a large number of issues about this "flicker", but it appears that many "flickering issues" may be caused by CSS being re-appended to DOM after the transition. This is not what I'm talking about as I don't have any problems with styles. The "flicker" I'm referring to is the HTML being re-rendered.
The best case scenario is that we have a simple page with a relatively easy DOM being rendered. In this case, the flicker is almost unnoticeable. In my case, my view is a bit complex. The rendering process can take some time because we are talking about tens of items, with subitems, and a lot of css. In my case, this flicker is unbearable, it just takes about 2 seconds for the page to become blank and back again. I'm just testing it with my computer, I don't even want to think about mobile devices :D
This behavior is especially noticeable with lazy routes. If you try the universal-starter, and if you navigate to the "lazy" route, put Chrome in "Slow 3g" mode and refresh the page clearing the cache, you can clearly see the problem. The same problem is unnoticeable in the main route (maybe because it's simpler? Or maybe it's because lazy routes make this flicker last longer?).
This is my problem. My page is not huge, but it's "slightly complex" and this flicker makes the Universal app unusable. I ask you: what are my options? Am I thinking/doing something wrong? I'm seriously thinking about using a loading spinner to hide the page until the client app has bootstrapped, but it's my last option since my app will be useful for SEO purposes but the user won't see anything in the meantime.
I have also tried using preboot (just the default configuration) which says that it helps to make the transition smoother, but nothing happened. I asked myself if it could be possible to make the client-app bootstrap in a hidden div and wait for it to be rendered before destroying the static HTML, but I didn't manage to get it right and I'm unsure if it would solve the problem.
Thank you so much for your patience and I hope this issue can make things clear for other people too, and hopefully solve their problems :)