7

I am trying to render my Angular 7 application with SSR using Angular Universal from this link https://angular.io/guide/universal. I had thought I had everything working from there since I was able to view the page source and see everything that is inside of the app.component rendered to the page. However, I am unable to render any of the routes inside of the router-outlet tag

I have simply stripped my app down to be doing exactly what the documentation says. I read in there something about ensuring ModuleMapLoaderModule is inside of the app.server.module in order for lazy loading to work. This does come default with the angular-cli command for universal so I am not sure what is going on.

app.server.module

import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';

import { AppComponent } from './app.component';
import { AppModule } from './app.module';

@NgModule({
  imports: [
    AppModule,
    ServerModule,
    ModuleMapLoaderModule,
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule {}

tsconfig.server.json

  "extends": "./tsconfig.app.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app-server",
  },
  "angularCompilerOptions": {
    "entryModule": "app/app.server.module#AppServerModule"
  }
}

server.ts

import 'zone.js/dist/zone-node';
import { enableProdMode } from '@angular/core';
// Express Engine
import { ngExpressEngine } from '@nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';

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 || 4000;
const DIST_FOLDER = join(process.cwd(), 'browser');

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main');

// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
}));

app.set('view engine', 'html');
app.set('views', DIST_FOLDER);

// Serve static files from /browser
app.get('*.*', express.static(DIST_FOLDER, {
  maxAge: '1y'
}));

// All regular routes use the Universal engine
app.get('*', (req, res) => {
  res.render(join(DIST_FOLDER, 'index.html'), { req });
});

// Start up the Node server
app.listen(PORT, () => {
  console.log(`Node Express server listening on http://localhost:${PORT}`);
});

Rendered output

<html lang="en">

<head>
    <base href="/">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- ...Rendered CSS styles... -->
<body>
    <app-root _nghost-sc0="" ng-version="7.2.15">
        <!-- ...Header code that has been rendered... -->

        <router-outlet _ngcontent-sc0=""></router-outlet> <!-- This is where I am expecting the content of the route to be -->

        <!-- ...Footer code that has been rendered... -->
    </app-root>
</body>

</html>

app.routing.module.ts

// Angular Imports

// Pages
// ...Page Imports..

const routes: Routes = [
  { path: '', component: HomePageComponent, pathMatch: 'full' },
  { path: 'events/:id', component: EventDetailPageComponent }, // This is the detail page I am testing with
  { path: 'unknown-error', component: UnknownErrorPageComponent },
  { path: '404', component: NotFoundPageComponent },
  { path: '**', redirectTo: '404' }
];

@NgModule({
  imports: [
    CommonModule,
    RouterModule.forRoot(routes),
  ],
  exports: [
    RouterModule,
  ]
})
export class AppRoutingModule { }

I have been having basically zero luck attempting to find accurate documentation on this for Angular 7 and any help would be greatly appreciated! Thanks

Manish Balodia
  • 1,863
  • 2
  • 23
  • 37
Midevilworm
  • 451
  • 5
  • 10
  • Can you please add your root routes to your code? – Brandon Taylor May 29 '19 at 21:29
  • @Brandon What do you mean by that? Do you want to see an example of the app.routing.ts file I have? – Midevilworm May 29 '19 at 21:33
  • 1
    Yes. I was curious as to which routes you were having problems with not rendering, and whether or not those routes were lazy loaded, if they had different router outlets, etc. – Brandon Taylor May 29 '19 at 21:34
  • @Brandon I updated the main body so it would be there now. I am really trying to test using that `/events/:id` route – Midevilworm May 29 '19 at 21:43
  • Ok, so definitely not lazy loaded and I'm assuming that it works when you're browsing the application running locally. To the best of my knowledge, the content of the component should be added to the page *after* the router outlet. – Brandon Taylor May 29 '19 at 21:59
  • Let me check one of my apps that has a similar setup and see where the content gets added – Brandon Taylor May 29 '19 at 21:59
  • Yeah, when browsing it works perfectly fine. The way I was testing the actual output of the SSR was using View Page Source – Midevilworm May 29 '19 at 22:13
  • Huh, I just checked one of my sites and it's doing the same thing you're describing, so apparently I have the same issue! – Brandon Taylor May 29 '19 at 22:17
  • Any error in the terminal server side? – David May 31 '19 at 07:19
  • @David this was actually it! I meant to update this post and forgot. But if you check the running terminal from the service there was an error that we just had ignored. Once we actually dug down into that we were able to target some of the components that we were using that did not play nice with SSR. Once we handled them it worked perfectly! Also, Brandon was correct in saying the component gets rendered after the router-outlet – Midevilworm Jun 26 '19 at 22:08
  • 1
    how did you solve this guys? I'm experiencing the same issue, the loader inside the app-root is not working when I build the angular universal app. Suggestions will be appreciated. Thanks! – BruneX Dec 20 '19 at 20:33

2 Answers2

0

Try add relativeLinkResolution: 'legacy'

@NgModule({
  imports: [RouterModule.forRoot(routes, {
    relativeLinkResolution: 'legacy'
})],
  exports: [RouterModule]
})
export class AppRoutingModule {
}

It helped to me.

Adam
  • 486
  • 1
  • 7
  • 19
-1

I do not have any experience with server side rendering, but a common issue with Angular apps when they are deployed to a server is the resolution of sub-directories. Basically the root will load but all other items that are handled in the angular routing will not work when accessed through the url. The web server is looking for those physical directories instead of allowing Angular routing to direct the page to a virtual directory. If this sounds like your issue there are some option outlined here

DanGo
  • 391
  • 1
  • 13
  • I don't know if this is the case. It runs perfectly fine when routing through it. It's when you inspect the page source to see what code was server-side rendered that it does not contain any of that routes template code. – Midevilworm May 29 '19 at 22:14