I'm doing SSR with TransferState and wondering who guarantees that when we do
http.get(...).subscribe(data => {
transferState.set(DATA_KEY, data)
})
data will be stored in transferState? Because http.get is async operation and content could be generate and provided to client without this data.
Angular Zone guarantees that all async operations (the calls tracked by zone.js ) to be finished before rendering.
Let's take a look at
server.ts
app.get('*', (req, res) => {
res.render('index', { req });
});
||
\/
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
We can see that all regular routes use the Universal engine to render the html.
res.render
method (1) defines default callback.
The ngExpressEngine function returns another function with that callback passed as parameter (2). As soon as that callback is triggered express sends the result to the user.
done = done || function (err, str) {
if (err) return req.next(err);
self.send(str);
};
Now let's see when that callback will be triggered. As mentioned before we need to look at ngExpressEngine function.
getFactory(moduleOrFactory, compiler)
.then(factory => {
return renderModuleFactory(factory, {
extraProviders
});
})
.then((html: string) => {
callback(null, html);
}, (err) => {
callback(err);
});
It will happen only after the promise(3), returning from renderModuleFactory
function, is resolved.
renderModuleFactory
function can be found at @angular/platform-server
export function renderModuleFactory<T>(
moduleFactory: NgModuleFactory<T>,
options: {document?: string, url?: string, extraProviders?: StaticProvider[]}):
Promise<string> {
const platform = _getPlatform(platformServer, options);
return _render(platform, platform.bootstrapModuleFactory(moduleFactory));
}
You can see above that we actually run Angular application here through platform.bootstrapModuleFactory(moduleFactory)
(4)
Inside _render function(5) application waits for bootstrapping to be finished
return moduleRefPromise.then((moduleRef) => {
and after that we can see the key for the answer:
return applicationRef.isStable.pipe((first((isStable: boolean) => isStable)))
.toPromise()
.then(() => {
You can see that angular universal looks at ApplicationRef.isStable observable to know when to finish rendering. In simple words isStable on ApplicationRef is triggered when Zone has no microtasks scheduled (7):
if (!zone.hasPendingMicrotasks) {
try {
zone.runOutsideAngular(() => zone.onStable.emit(null));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With