Cannot get SSR to work when using Angular Universal with pages that use dynamic content. All other pages work and the dynamic pages return with HTML but do not include the dynamic data.
Method Called:
ngOnInit() {
const div = this.renderer.createElement('div');
const texttest = this.renderer.createText('this works');
this.renderer.appendChild(div, texttest);
this.renderer.appendChild(this.content.nativeElement, div);
this.createLinkForCanonicalURL();
// The HTML never shows, the method works fine.
this._contentfulService.getBlogPosts().then(posts => {
this.blogPosts = _.orderBy(posts, ['sys.createdAt'], ['desc']);
});
}
Service Method
getBlogPosts(query ? : object): Promise<Entry<any>[]> {
return this.cdaClient.getEntries(Object.assign({
content_type: CONFIG.contentTypeIds.blogPosts
}, {
'fields.private': false
}))
.then(res => res.items);
}
Template:
This never shows in the source.
<li class="blog-post" *ngFor="let post of blogPosts">
<h1>{{post.fields.title}}</h1>
</li>
Have tried the starter kits and they do not work calling the same method. Also tried render2 to inject and a resolver service to test fetching data before the load.
Nothing seems to work?
Any help would be appreciated!
EDIT
This is my server.ts file
// These are important and needed before anything else
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import { enableProdMode } from '@angular/core';
import * as express from 'express';
import { join } from 'path';
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
// Express server
const app = express();
const PORT = process.env.PORT || 8080;
const DIST_FOLDER = join(process.cwd(), 'dist');
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } =
require('./dist/server/main');
// Express Engine
import { ngExpressEngine } from '@nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));
// TODO: implement data requests securely
app.get('/api/*', (req, res) => {
res.status(404).send('data requests are not supported');
});
// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));
// All regular routes use the Universal engine
app.get('*', (req, res) => {
res.render('index', { req });
});
// Start up the Node server
app.listen(PORT, () => {
console.log(`Node server listening on http://localhost:${PORT}`);
});
and my app server module
@NgModule({
bootstrap: [AppComponent],
imports: [
BrowserModule.withServerTransition({ appId: 'app-root' }),
AppModule,
ModuleMapLoaderModule,
ServerModule,
NoopAnimationsModule,
ServerTransferStateModule, // comment
]
})
export class AppServerModule { }
It seems that angular universal doesn't wait for async calls to render.
I had the same problem and finally made it work using ZoneMacroTaskWrapper as mentioned in this issue.
This is the service I made.
You need use TransferHttpCacheModule for wait HTTP call to API or use another implementation that work with TransferState like ngx-transfer-http .
My sample with wait 3-second delay: https://github.com/Angular-RU/angular-universal-starter/blob/master/src/app/transfer-back/transfer-back.component.ts
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