Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 { }
like image 643
Smokey Dawson Avatar asked Sep 11 '18 07:09

Smokey Dawson


2 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.

like image 62
Alvaro Avatar answered Oct 10 '22 07:10

Alvaro


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

like image 39
gorniv Avatar answered Oct 10 '22 08:10

gorniv