(Angular 6) Angular Universal - Not waiting on content API call

Viewed 5820

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 { }
3 Answers

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.

Try to edit your code as follows:

async 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();

  const posts = await this._contentfulService.getBlogPosts();
  this.blogPosts = _.orderBy(posts, ['sys.createdAt'], ['desc'])

 }
Related