-
Notifications
You must be signed in to change notification settings - Fork 50
Big FOUC as server side styles removed before the switch to client side #75
Comments
We're looking into this, but it looks like this may be an issue with Angular core. We'll update this as the issue develops. |
This is a huge issue for me. Where can we expect more info on this? |
@xban1x The Angular core team is looking into this, but I don't have an ETA for you on a resolution. |
@CaerusKaru any news here? |
None yet, still investigating. |
@CaerusKaru just pinging... |
Ping! |
I am experiencing this bug too. But, actually, not on the latest versions: Angular v.5.2.5, Preboot v.6.0.0-beta.3) |
Here is how I fixed this issue for my project : I removed the import { DOCUMENT, ɵgetDOM, ɵTRANSITION_ID } from '@angular/platform-browser';
import { APP_BOOTSTRAP_LISTENER, APP_ID, ModuleWithProviders, NgModule, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
export function removeStyleTags(document: HTMLDocument, platformId: string): Function {
return () => {
if (isPlatformBrowser(platformId)) {
const dom = ɵgetDOM();
const styles: HTMLElement[] = Array.prototype.slice.apply(dom.querySelectorAll(document, 'style[ng-transition]'));
document.addEventListener('PrebootComplete', () => {
setTimeout(() => styles.forEach(el => dom.remove(el)));
});
}
};
}
@NgModule({})
export class ServerTransition {
public static forRoot(param: { appId: string }): ModuleWithProviders {
return {
ngModule: ServerTransition,
providers: [
{ provide: APP_ID, useValue: param.appId },
{ provide: ɵTRANSITION_ID, useValue: param.appId },
{
provide: APP_BOOTSTRAP_LISTENER,
useFactory: removeStyleTags,
deps: [DOCUMENT, PLATFORM_ID],
multi: true
}
]
};
}
} As you can see, I remove the style tags when the event PrebootComplete is raised. No more flickering. @NgModule({
imports: [
...,
BrowserModule,
ServerTransition.forRoot({ appId: 'hg-customer' }),
PrebootModule.withConfig({ appRoot: 'hg-root', replay: false, eventSelectors })
],
declarations: [
AppComponent,
...
]
})
export class AppModule {} |
@Willovent On safari on page reload from browser refresh, the entire dom is being deleted. Did you come across this issue? |
I did actually, I've post an issue for that (#91). However this has nothing to do with the transition, but more with the preboot buffer. To avoid it, I delayed my application bootstrap by 10ms after investigating this issue and evrything work's fine. document.addEventListener('DOMContentLoaded', () => {
setTimeout(
() =>
platformBrowserDynamic()
.bootstrapModule(AppBrowserModule)
.catch(err => console.log(err)),
10
);
}); However I had this issue on all browsers, so it may not be related to yours. Edit: It is fix on beta-5. I removed the workaround on my application |
Hi @Willovent the fix you are talking about is in the beta-5 of what? I did see the flashing |
@Maqix I was talking about the entire DOM being deleted. This was a response to SwamyKottur question. I still have the code for the |
Thanks for the workaround @Willovent. It's solving this issue, but it breaks the Angular server transfer state. Because <html>
<head>
[...]
<style ng-transition="my-app">.foo[_ngcontent-sc0] {}</style>
[...]
</head>
<body>
[...]
<script id="gmx-state" type="application/json">{&q;G.https://...}</script>
^-> gmx is randomly generated. It should be my-app-state
</body>
</html> I fixed it by keeping the {
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 am facing the same issue. @Willovent your workaround is not working with [email protected] and [email protected]. Can you please help?
|
I sidestepped this issue by adding a "loader" to cover the FOUC.
Add this at the start of the
Add this code to the
As the FOUC just happens on the first "load", this solves the issue with a "cover" without fancy coding |
@CaerusKaru is there an update planned for this issue, either before or after Ivy releases? |
Not sure if it's the same thing but getting some weird behaviour and am not sure whether to open a different issue or not. AppServerModule.imports contains: BrowserModule.withServerTransition({ appId: 'meh' }),
PrebootModule.withConfig({ appRoot: 'cw-root' }), Server delivers index.html like this: <style ng-transition="meh">.router-outlet-main[_ngcontent-sc1] { ... }</style>
<script class="preboot-inline-script">var prebootInitFn = (function() { ... })();</script>
...
<cw-root _nghost-sc0="" ng-version="7.2.14">
<script class="preboot-inline-script">prebootInitFn();</script>
...
</cw-root>
...
<script id="meh-state" type="application/json">{}</script></body></html> As soon as the JS is downloaded, the DOM contains something like this: <cw-root _nghost-sc0="" ng-version="7.2.14" _nghost-meh-c0="" style="display: none;">
... contents not removed ...
</cw-root>
<cw-root _nghost-sc0="" ng-version="7.2.14" ng-non-bindable="">
...
</cw-root> All of the styles are for _nghost-meh-c0 so the secondary added Not replicable locally when building browser/server but happens on the server when there's a long gap between universal content delivered and client JS download |
I had the same issue as @intellix and I was able to fix it by injecting the
|
Update on this issue? |
* cssチラツキ問題などがあった。 angular/preboot#75
Can we add @malegrin solution to Preboot module? We could just provide that app initializer in PrebootModule for angular? |
anyone there? |
There are some comments about this being an Angular core issue, but there isn't a link to the issue in Was it angular/angular#15716? However, there appear to be a lot of comments that using that option is not enough. Some have commented that they need this Preboot library to solve the issue in addition to that router config. Looking at https://github.com/mgol/angular-server-bug, it appears to be based on Angular There appear to be some related issues in
So it seems like this issue is not resolved, but I can't find any open Angular core issue to track the problem or any details of how Angular core is involved. |
I had this problem with 8.0.1, and router config is not enough.
|
@malengrin's solution mostly worked... background-images still flicker (load in and out, but since it's background-images, it doesn't affect overall layout). It's still 1000x better than it was! |
Any updates? |
dom.querySelectorAll doesn't work, dom doesn't contain such a property, could this be an angular 9 thing? |
No |
Then I rephrase my question: how come I get this error for this fix? edit: edit2: |
Hello guys, just for our own interest? Do you think this will get addressed anytime soon? To me it seems a very important issue and wouldmake the actual SSR experience much better overall. Many thanks! |
@CarlosTorrecillas I haven't seen any indication that this will be resolved "soon". I agree that it's important for most SSR/Universal apps. |
In My case I also needed to wrap the replay in a
|
It's the 3 year anniversary that Preboot doesn't support Angular and seems it has been abandoned. I think the only solution is to just display an old-school loader or fade-out overlay on top of the content to prevent users clicking links and opening new tabs etc. Is this supported in other libraries like Scully? I've always been on-board with how Angular provide everything out of the box: Animations, Forms, Universal rendering but it just means you come to rely on official components that get abandoned (like forms) |
This project has not been abandoned and fully supports Angular v11 (latest). This issue is the only major issue, and doesn't affect all users. PRs are welcome to fix this issue, unfortunately it's low on our current list of priorities. |
FYI I was profiling our app's performance & looking into ways to optimize it, and I've realized @malengrin's fix is causing an unnecessary style recalculation of the whole The app is initially rendered on a hidden In my case removing the server styles synchronously does not create a FOUC (since the element has been swapped anyway), and removes the 3rd unnecessary style recalculation, improving the TBT & TTI (as this happened to be the task long task). In short the updated fix is: {
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
styles.forEach(el => dom.remove(el));
});
}
};
},
deps: [DOCUMENT, PLATFORM_ID],
multi: true
} |
I finally resolved this issue after almost a year. So here is my solution for anyone having the same problem. It was only happening when I was using SSR for pages behind authentication in my case. Client-side hydration most likely happens after the application is stable (
Which was exactly the reason why my app did not load in SSR because I set an interval for polling user notifications in a service. There are some solutions for this: start interval after the app is stable, start interval in |
Note: for support questions, please use the Universal Slack Channcel or https://gitter.im/angular/universal
A bug.
Server-side styles are in some scenarios removed by Angular before Preboot switches the app to the client-side one, causing a potentially huge flash of unstyled content.
There should be no visible app swap. Styles shouldn't disappear before the app is switched to the client side one.
Current behavior looks really broken to the end user.
This issue happens if a component is hidden behind
*ngIf
and the condition is set to a truthy one only after some time - in particular, when waiting for a specific HTTP request to finish. Angular thinks the client side app is ready as it bootstrapped correctly and it just waits for the HTTP request to finish to swap the flag. However, Preboot still waits for the app to get stable so it shows the server-side generated one. Unfortunately, its inline styles are already removed and the new ones are not loaded yet as Angular haven't created the component yet client side.The text was updated successfully, but these errors were encountered: