Angular 7 SSR - problems with NgZone












8















I've recently moved my company's website from React to Angular, since most our projects were already on Angular 7. Being the "use-the-latest-and-greatest" person that I am, I decided to implement server-side rendering to get the google page speed rating close to that 100/100 (currently 42/100). I've been tinkering with it for the better part of the week now, unsuccessfully - the latest roadblock has been especially hard for me to overcome. Here's a brief info about my setup, then I'll get into the details:




  • NodeJS 8.9.1

  • Angular 7 latest

  • Webpack 4.26.0

  • @ngtools/webpack 7.0.5

  • Not using angular-cli

  • AoT setup

  • single page app


This is the error I'm getting when I try to render the layout.html file that's set up for SSR:



TypeError: Cannot read property 'subscribe' of undefined
at new ApplicationRef (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:43263:37)
at _createClass (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46296:20)
at _createProviderInstance (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46258:26)
at initNgModule (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46190:32)
at new NgModuleRef_ (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46918:9)
at createNgModuleRef (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46907:12)
at Object.debugCreateNgModuleRef [as createNgModuleRef] (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:48738:12)
at NgModuleFactory_.module.exports.NgModuleFactory_.create (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:49466:25)
at C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14656:47
at ZoneDelegate.module.exports.ZoneDelegate.invoke (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:139510:26)
at Object.onInvoke (C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14194:37)
at ZoneDelegate.module.exports.ZoneDelegate.invoke (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:139509:32)
at Zone.module.exports.Zone.run (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:139260:43)
at NgZone.run (C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14108:32)
at PlatformRef.bootstrapModuleFactory (C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14654:27)
at renderModuleFactory (C:codelemmsoftWebsitereponode_modules@angularplatform-serverbundlesplatform-server.umd.js:1033:43)
at View.module.app.engine (C:codelemmsoftWebsiterepomodulesclientssitelayoutindex.js:60:4)
at View.render (C:codelemmsoftWebsitereponode_modulesexpresslibview.js:135:8)
at tryRender (C:codelemmsoftWebsitereponode_modulesexpresslibapplication.js:640:10)
at Function.render (C:codelemmsoftWebsitereponode_modulesexpresslibapplication.js:592:3)
at ServerResponse.render (C:codelemmsoftWebsitereponode_modulesexpresslibresponse.js:1008:7)
at C:codelemmsoftWebsiterepomodulesclientssitelayoutindex.js:83:9


After much reading through the main.js bundle file, I've pinpointed the problem to the following:






var ApplicationRef = /** @class */ (function () {
/** @internal */
function ApplicationRef(_zone, _console, _injector, _exceptionHandler, _componentFactoryResolver, _initStatus) {
var _this = this;
this._zone = _zone; // in this method, the _zone argument is {}, so there is no onMicrotaskEmpty method in it => when this._zone.onMicrotaskEmpty.subscribe() is attempted, we get "Cannot read property 'subscribe' of undefined"
this._console = _console;
this._injector = _injector;
this._exceptionHandler = _exceptionHandler;
this._componentFactoryResolver = _componentFactoryResolver;
this._initStatus = _initStatus;
this._bootstrapListeners = ;
this._views = ;
this._runningTick = false;
this._enforceNoNewChanges = false;
this._stable = true;
// more code
}
// more code
}





In this method, the _zone argument is {}, so there is no onMicrotaskEmpty method in it => when this._zone.onMicrotaskEmpty.subscribe() is attempted, we get "Cannot read property 'subscribe' of undefined". I kept on digging through the stack trace - this the previous step, where new ApplicationRef is invoked and _zone is passed as {}:






function _createClass(ngModule, ctor, deps) {
var len = deps.length;
switch (len) {
case 0:
return new ctor();
case 1:
return new ctor(resolveNgModuleDep(ngModule, deps[0]));
case 2:
return new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]));
case 3:
return new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]), resolveNgModuleDep(ngModule, deps[2]));
default:
// this is where we get some insight into the cause of the error
var depValues = new Array(len);
for (var i = 0; i < len; i++) {
depValues[i] = resolveNgModuleDep(ngModule, deps[i]);
}
// if we do console.log(deps[0], depValues[0]), which is _zone, we get interesting stuff...
return new (ctor.bind.apply(ctor, Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__spread"])([void 0], depValues)))();
}
}





This is where we get some insight into the cause of the error - in the 'default' block of the switch. If we console.log(deps[0], depValues[0]) after the for loop, which is _zone, we get interesting stuff:






// deps[0]
{ flags: 0,
token:
{ [Function: NgZone]
isInAngularZone: [Function],
assertInAngularZone: [Function],
assertNotInAngularZone: [Function] },
tokenKey: 'NgZone_29' }
// depValues[0]
{}





So, here's the culprit, I thought! 'resolveNgModuleDep' screws it up! So I kept on digging:






function resolveNgModuleDep(data, depDef, notFoundValue) {
if (notFoundValue === void 0) { notFoundValue = Injector.THROW_IF_NOT_FOUND; }
var former = setCurrentInjector(data);
try {
if (depDef.flags & 8 /* Value */) {
return depDef.token;
}
if (depDef.flags & 2 /* Optional */) {
notFoundValue = null;
}
if (depDef.flags & 1 /* SkipSelf */) {
return data._parent.get(depDef.token, notFoundValue);
}
var tokenKey_1 = depDef.tokenKey;
switch (tokenKey_1) {
case InjectorRefTokenKey:
case INJECTORRefTokenKey:
case NgModuleRefTokenKey:
return data;
}
var providerDef = data._def.providersByKey[tokenKey_1];
var injectableDef = void 0;
if (providerDef) {
var providerInstance = data._providers[providerDef.index];
if (providerInstance === undefined) {
providerInstance = data._providers[providerDef.index] =
_createProviderInstance(data, providerDef);
}
return providerInstance === UNDEFINED_VALUE ? undefined : providerInstance;
}
else if ((injectableDef = getInjectableDef(depDef.token)) && targetsModule(data, injectableDef)) {
var index = data._providers.length;
data._def.providersByKey[depDef.tokenKey] = {
flags: 1024 /* TypeFactoryProvider */ | 4096 /* LazyProvider */,
value: injectableDef.factory,
deps: , index: index,
token: depDef.token,
};
data._providers[index] = UNDEFINED_VALUE;
return (data._providers[index] =
_createProviderInstance(data, data._def.providersByKey[depDef.tokenKey]));
}
else if (depDef.flags & 4 /* Self */) {
return notFoundValue;
}
// there it is!
return data._parent.get(depDef.token, notFoundValue);
}
finally {
setCurrentInjector(former);
}
}





There it is! Right before finally, on the line that returns data._parent.get(depDef.token, notFoundValue) - this is where depDef.token (which in our case is NgZone) is passed, notFoundValue is null. The returned object is just {}, hence all the troubles later on.
This is as far as I've managed to get, I've been going back and forth trying to solve it from here, but to no avail. Believe me, I've searched through and through in stackoverflow and just in google; I've read a 1000 medium posts - no success. I don't use angular-cli because I like to customize my webpack config, among other things, but I doubt that's the reason, because angular-cli itself uses webpack under the hood. I'm going to paste a few additional stuff below in several snippets - my webpack config, the server method where the html and angular bundle is rendered, etc.






// the webpack config

'use strict'

const
AngularCompilerPlugin = require( "@ngtools/webpack" ).AngularCompilerPlugin,
BellOnBundlerErrorPlugin = require('bell-on-bundler-error-plugin'),
BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin,
path = require('path'),
ProgressBarPlugin = require('progress-bar-webpack-plugin'),
UglifyJsPlugin = require('uglifyjs-webpack-plugin'),
webpack = require('webpack')


module.exports = (config, name) => {
let includePath = config.clientPath,
publicPath = path.join(config.publicPath, 'serverBuild'),
libPath = path.join(__dirname, '../../lib'),
nodeModulesPath = config.nodeModulesPath,
include = [includePath]

return {
target: 'node',
mode: 'none',
entry: [
path.join(includePath, 'polyfills.ts'),
path.join(includePath, 'vendor.common.ts'),
path.join(includePath, 'vendor.server.ts'),
path.join(includePath, 'index.server.ts')
],
output: {
path: publicPath,
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '/dist/',
libraryTarget: 'commonjs-module'
},
resolve: {
extensions: ['.ts', '.js'],
modules: ['node_modules', libPath]
},
module: {
rules: [
{
test: /.pug$/,
include: [libPath, includePath],
use: ['raw-loader', 'pug-html-loader']
},
{
test: /.css$/,
include: [libPath, nodeModulesPath, includePath],
exclude: ,
use: ['to-string-loader', 'css-loader']
},
{
test: /.less$/,
exclude: ,
use: ['to-string-loader', 'css-loader', 'less-loader']
},
{
test: /.scss$/,
include: [libPath, nodeModulesPath, includePath],
exclude: ,
use: ['raw-loader', 'sass-loader']
},
{
test: /(?:.ngfactory.js|.ngstyle.js|.ts)$/,
include: [includePath, libPath],
use: [{
loader: '@ngtools/webpack'
}],
exclude: [/.(spec|e2e).ts$/]
},
{
test: /.json$/,
include,
exclude: ,
use: ["json2-loader"]
}
]
},
stats: 'verbose',
plugins: [
// new webpack.HotModuleReplacementPlugin(),
new BellOnBundlerErrorPlugin(),
new ProgressBarPlugin({
format: ' build [:bar] (:percent) - (:elapsed seconds)',
clear: false,
complete: '#',
summary: 'true'
}),
// new webpack.NamedModulesPlugin(),
new AngularCompilerPlugin({
tsConfigPath: path.join(__dirname, '../../tsconfig.server.json'),
entryModule: path.join(includePath, 'app.server.ts#AppServerModule'),
sourceMap: true
})
]
}
}








// tsconfig.json and tsconfig.server.json
{
"compilerOptions": {
"baseUrl": ".",
"target": "es6",
"module": "es2015",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"importHelpers": true,
"strictNullChecks": false,
"lib": [
"es2015",
"dom"
],
"typeRoots": [
"node_modules/@types",
"typings"
],
"types": [
"hammerjs",
"node"
],
"paths": {
"ramster-ui/*": ["lib/ramster-ui/*"]
}
},
"include": [
"clients/**/*",
"lib/ramster-ui/**/*"
],
"exclude": [
"clients/**/*.spec.ts",
"clients/**/*.e2e.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}


{
"extends": "./tsconfig.json",
"include": [
"clients/**/polyfills.ts",
"clients/**/vendor.common.ts",
"clients/**/vendor.server.ts",
"clients/**/app.server.ts",
"clients/**/index.server.ts",
],
"exclude": [
"clients/**/index.ts",
"clients/**/vendor.browser.ts",
"clients/**/app.ts",
"clients/**/*.spec.ts",
"clients/**/*.e2e.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}








// excerpts from my server setup

// this method is called before the server is started
setup() {
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require(path.join(__dirname, '../../../../public/site/serverBuild', 'main'))
// LAZY_MODULE_MAP is undefined for now
this.module.app.engine('html', (_, options, callback) => {
renderModuleFactory(
AppServerModuleNgFactory, {}
).then((html) => callback(null, html), (error) => callback(error))
})
this.module.app.set('view engine', 'html')
}

// the method returned by "loadLayout" is mounted in expressjs
loadLayout() {
const {module} = this
return function* (req, res, next) {
try {
res.render(path.join('../../../../public/site/layout.html'), {req, res})
} catch (e) {
req.locals.error = e
next()
}
}
}








// polyfills.ts
import 'core-js/es6'
import 'reflect-metadata'


// vendor.common.ts
import 'rxjs/add/operator/first'
import 'rxjs/add/operator/toPromise'
import 'popper.js'

import '@angular/common'
import '@angular/core'
import '@angular/flex-layout'
import '@angular/forms'
import '@angular/material'
import '@angular/router'


// vendor.server.ts
import 'zone.js/dist/zone-node'
import '@angular/platform-server'


// index.server.ts
import {enableProdMode} from '@angular/core'
if (process.env.NODE_ENV === 'production') {
enableProdMode()
}
export * from './app.server.ngfactory'


// app.server.ts
import {NgModule} from '@angular/core'
import {ServerModule} from '@angular/platform-server'

@NgModule({
imports: [
ServerModule
],
exports: ,
declarations: ,
providers: ,
bootstrap:
})
class AppServerModule {}

export {AppServerModule}





You'll see that I've trimmed down the server-side app to the very basics, so I can eliminate the error being caused by something I've written.
Any help would be MUCH appreciated.










share|improve this question

























  • Just a shot in the dark, can you move import 'zone.js/dist/zone-node' to the top, as in include it as the very first import.

    – Phix
    Nov 25 '18 at 17:42











  • Thanks :) It's the first thing I tried - it was on top of the polyfills file before. Sadly, no difference :(

    – Rumen Rumenov
    Nov 25 '18 at 18:14











  • What the context of the original throw: ...clientssitelayoutindex.js:83:9 ?

    – Phix
    Nov 25 '18 at 18:22











  • It's the rendering line from my server setup snippet - res.render(path.join('../../../../public/site/layout.html'), {req, res})

    – Rumen Rumenov
    Nov 25 '18 at 19:24











  • Can you provide a minimal reproduction on github?

    – yurzui
    Nov 26 '18 at 9:15
















8















I've recently moved my company's website from React to Angular, since most our projects were already on Angular 7. Being the "use-the-latest-and-greatest" person that I am, I decided to implement server-side rendering to get the google page speed rating close to that 100/100 (currently 42/100). I've been tinkering with it for the better part of the week now, unsuccessfully - the latest roadblock has been especially hard for me to overcome. Here's a brief info about my setup, then I'll get into the details:




  • NodeJS 8.9.1

  • Angular 7 latest

  • Webpack 4.26.0

  • @ngtools/webpack 7.0.5

  • Not using angular-cli

  • AoT setup

  • single page app


This is the error I'm getting when I try to render the layout.html file that's set up for SSR:



TypeError: Cannot read property 'subscribe' of undefined
at new ApplicationRef (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:43263:37)
at _createClass (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46296:20)
at _createProviderInstance (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46258:26)
at initNgModule (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46190:32)
at new NgModuleRef_ (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46918:9)
at createNgModuleRef (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46907:12)
at Object.debugCreateNgModuleRef [as createNgModuleRef] (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:48738:12)
at NgModuleFactory_.module.exports.NgModuleFactory_.create (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:49466:25)
at C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14656:47
at ZoneDelegate.module.exports.ZoneDelegate.invoke (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:139510:26)
at Object.onInvoke (C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14194:37)
at ZoneDelegate.module.exports.ZoneDelegate.invoke (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:139509:32)
at Zone.module.exports.Zone.run (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:139260:43)
at NgZone.run (C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14108:32)
at PlatformRef.bootstrapModuleFactory (C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14654:27)
at renderModuleFactory (C:codelemmsoftWebsitereponode_modules@angularplatform-serverbundlesplatform-server.umd.js:1033:43)
at View.module.app.engine (C:codelemmsoftWebsiterepomodulesclientssitelayoutindex.js:60:4)
at View.render (C:codelemmsoftWebsitereponode_modulesexpresslibview.js:135:8)
at tryRender (C:codelemmsoftWebsitereponode_modulesexpresslibapplication.js:640:10)
at Function.render (C:codelemmsoftWebsitereponode_modulesexpresslibapplication.js:592:3)
at ServerResponse.render (C:codelemmsoftWebsitereponode_modulesexpresslibresponse.js:1008:7)
at C:codelemmsoftWebsiterepomodulesclientssitelayoutindex.js:83:9


After much reading through the main.js bundle file, I've pinpointed the problem to the following:






var ApplicationRef = /** @class */ (function () {
/** @internal */
function ApplicationRef(_zone, _console, _injector, _exceptionHandler, _componentFactoryResolver, _initStatus) {
var _this = this;
this._zone = _zone; // in this method, the _zone argument is {}, so there is no onMicrotaskEmpty method in it => when this._zone.onMicrotaskEmpty.subscribe() is attempted, we get "Cannot read property 'subscribe' of undefined"
this._console = _console;
this._injector = _injector;
this._exceptionHandler = _exceptionHandler;
this._componentFactoryResolver = _componentFactoryResolver;
this._initStatus = _initStatus;
this._bootstrapListeners = ;
this._views = ;
this._runningTick = false;
this._enforceNoNewChanges = false;
this._stable = true;
// more code
}
// more code
}





In this method, the _zone argument is {}, so there is no onMicrotaskEmpty method in it => when this._zone.onMicrotaskEmpty.subscribe() is attempted, we get "Cannot read property 'subscribe' of undefined". I kept on digging through the stack trace - this the previous step, where new ApplicationRef is invoked and _zone is passed as {}:






function _createClass(ngModule, ctor, deps) {
var len = deps.length;
switch (len) {
case 0:
return new ctor();
case 1:
return new ctor(resolveNgModuleDep(ngModule, deps[0]));
case 2:
return new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]));
case 3:
return new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]), resolveNgModuleDep(ngModule, deps[2]));
default:
// this is where we get some insight into the cause of the error
var depValues = new Array(len);
for (var i = 0; i < len; i++) {
depValues[i] = resolveNgModuleDep(ngModule, deps[i]);
}
// if we do console.log(deps[0], depValues[0]), which is _zone, we get interesting stuff...
return new (ctor.bind.apply(ctor, Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__spread"])([void 0], depValues)))();
}
}





This is where we get some insight into the cause of the error - in the 'default' block of the switch. If we console.log(deps[0], depValues[0]) after the for loop, which is _zone, we get interesting stuff:






// deps[0]
{ flags: 0,
token:
{ [Function: NgZone]
isInAngularZone: [Function],
assertInAngularZone: [Function],
assertNotInAngularZone: [Function] },
tokenKey: 'NgZone_29' }
// depValues[0]
{}





So, here's the culprit, I thought! 'resolveNgModuleDep' screws it up! So I kept on digging:






function resolveNgModuleDep(data, depDef, notFoundValue) {
if (notFoundValue === void 0) { notFoundValue = Injector.THROW_IF_NOT_FOUND; }
var former = setCurrentInjector(data);
try {
if (depDef.flags & 8 /* Value */) {
return depDef.token;
}
if (depDef.flags & 2 /* Optional */) {
notFoundValue = null;
}
if (depDef.flags & 1 /* SkipSelf */) {
return data._parent.get(depDef.token, notFoundValue);
}
var tokenKey_1 = depDef.tokenKey;
switch (tokenKey_1) {
case InjectorRefTokenKey:
case INJECTORRefTokenKey:
case NgModuleRefTokenKey:
return data;
}
var providerDef = data._def.providersByKey[tokenKey_1];
var injectableDef = void 0;
if (providerDef) {
var providerInstance = data._providers[providerDef.index];
if (providerInstance === undefined) {
providerInstance = data._providers[providerDef.index] =
_createProviderInstance(data, providerDef);
}
return providerInstance === UNDEFINED_VALUE ? undefined : providerInstance;
}
else if ((injectableDef = getInjectableDef(depDef.token)) && targetsModule(data, injectableDef)) {
var index = data._providers.length;
data._def.providersByKey[depDef.tokenKey] = {
flags: 1024 /* TypeFactoryProvider */ | 4096 /* LazyProvider */,
value: injectableDef.factory,
deps: , index: index,
token: depDef.token,
};
data._providers[index] = UNDEFINED_VALUE;
return (data._providers[index] =
_createProviderInstance(data, data._def.providersByKey[depDef.tokenKey]));
}
else if (depDef.flags & 4 /* Self */) {
return notFoundValue;
}
// there it is!
return data._parent.get(depDef.token, notFoundValue);
}
finally {
setCurrentInjector(former);
}
}





There it is! Right before finally, on the line that returns data._parent.get(depDef.token, notFoundValue) - this is where depDef.token (which in our case is NgZone) is passed, notFoundValue is null. The returned object is just {}, hence all the troubles later on.
This is as far as I've managed to get, I've been going back and forth trying to solve it from here, but to no avail. Believe me, I've searched through and through in stackoverflow and just in google; I've read a 1000 medium posts - no success. I don't use angular-cli because I like to customize my webpack config, among other things, but I doubt that's the reason, because angular-cli itself uses webpack under the hood. I'm going to paste a few additional stuff below in several snippets - my webpack config, the server method where the html and angular bundle is rendered, etc.






// the webpack config

'use strict'

const
AngularCompilerPlugin = require( "@ngtools/webpack" ).AngularCompilerPlugin,
BellOnBundlerErrorPlugin = require('bell-on-bundler-error-plugin'),
BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin,
path = require('path'),
ProgressBarPlugin = require('progress-bar-webpack-plugin'),
UglifyJsPlugin = require('uglifyjs-webpack-plugin'),
webpack = require('webpack')


module.exports = (config, name) => {
let includePath = config.clientPath,
publicPath = path.join(config.publicPath, 'serverBuild'),
libPath = path.join(__dirname, '../../lib'),
nodeModulesPath = config.nodeModulesPath,
include = [includePath]

return {
target: 'node',
mode: 'none',
entry: [
path.join(includePath, 'polyfills.ts'),
path.join(includePath, 'vendor.common.ts'),
path.join(includePath, 'vendor.server.ts'),
path.join(includePath, 'index.server.ts')
],
output: {
path: publicPath,
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '/dist/',
libraryTarget: 'commonjs-module'
},
resolve: {
extensions: ['.ts', '.js'],
modules: ['node_modules', libPath]
},
module: {
rules: [
{
test: /.pug$/,
include: [libPath, includePath],
use: ['raw-loader', 'pug-html-loader']
},
{
test: /.css$/,
include: [libPath, nodeModulesPath, includePath],
exclude: ,
use: ['to-string-loader', 'css-loader']
},
{
test: /.less$/,
exclude: ,
use: ['to-string-loader', 'css-loader', 'less-loader']
},
{
test: /.scss$/,
include: [libPath, nodeModulesPath, includePath],
exclude: ,
use: ['raw-loader', 'sass-loader']
},
{
test: /(?:.ngfactory.js|.ngstyle.js|.ts)$/,
include: [includePath, libPath],
use: [{
loader: '@ngtools/webpack'
}],
exclude: [/.(spec|e2e).ts$/]
},
{
test: /.json$/,
include,
exclude: ,
use: ["json2-loader"]
}
]
},
stats: 'verbose',
plugins: [
// new webpack.HotModuleReplacementPlugin(),
new BellOnBundlerErrorPlugin(),
new ProgressBarPlugin({
format: ' build [:bar] (:percent) - (:elapsed seconds)',
clear: false,
complete: '#',
summary: 'true'
}),
// new webpack.NamedModulesPlugin(),
new AngularCompilerPlugin({
tsConfigPath: path.join(__dirname, '../../tsconfig.server.json'),
entryModule: path.join(includePath, 'app.server.ts#AppServerModule'),
sourceMap: true
})
]
}
}








// tsconfig.json and tsconfig.server.json
{
"compilerOptions": {
"baseUrl": ".",
"target": "es6",
"module": "es2015",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"importHelpers": true,
"strictNullChecks": false,
"lib": [
"es2015",
"dom"
],
"typeRoots": [
"node_modules/@types",
"typings"
],
"types": [
"hammerjs",
"node"
],
"paths": {
"ramster-ui/*": ["lib/ramster-ui/*"]
}
},
"include": [
"clients/**/*",
"lib/ramster-ui/**/*"
],
"exclude": [
"clients/**/*.spec.ts",
"clients/**/*.e2e.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}


{
"extends": "./tsconfig.json",
"include": [
"clients/**/polyfills.ts",
"clients/**/vendor.common.ts",
"clients/**/vendor.server.ts",
"clients/**/app.server.ts",
"clients/**/index.server.ts",
],
"exclude": [
"clients/**/index.ts",
"clients/**/vendor.browser.ts",
"clients/**/app.ts",
"clients/**/*.spec.ts",
"clients/**/*.e2e.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}








// excerpts from my server setup

// this method is called before the server is started
setup() {
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require(path.join(__dirname, '../../../../public/site/serverBuild', 'main'))
// LAZY_MODULE_MAP is undefined for now
this.module.app.engine('html', (_, options, callback) => {
renderModuleFactory(
AppServerModuleNgFactory, {}
).then((html) => callback(null, html), (error) => callback(error))
})
this.module.app.set('view engine', 'html')
}

// the method returned by "loadLayout" is mounted in expressjs
loadLayout() {
const {module} = this
return function* (req, res, next) {
try {
res.render(path.join('../../../../public/site/layout.html'), {req, res})
} catch (e) {
req.locals.error = e
next()
}
}
}








// polyfills.ts
import 'core-js/es6'
import 'reflect-metadata'


// vendor.common.ts
import 'rxjs/add/operator/first'
import 'rxjs/add/operator/toPromise'
import 'popper.js'

import '@angular/common'
import '@angular/core'
import '@angular/flex-layout'
import '@angular/forms'
import '@angular/material'
import '@angular/router'


// vendor.server.ts
import 'zone.js/dist/zone-node'
import '@angular/platform-server'


// index.server.ts
import {enableProdMode} from '@angular/core'
if (process.env.NODE_ENV === 'production') {
enableProdMode()
}
export * from './app.server.ngfactory'


// app.server.ts
import {NgModule} from '@angular/core'
import {ServerModule} from '@angular/platform-server'

@NgModule({
imports: [
ServerModule
],
exports: ,
declarations: ,
providers: ,
bootstrap:
})
class AppServerModule {}

export {AppServerModule}





You'll see that I've trimmed down the server-side app to the very basics, so I can eliminate the error being caused by something I've written.
Any help would be MUCH appreciated.










share|improve this question

























  • Just a shot in the dark, can you move import 'zone.js/dist/zone-node' to the top, as in include it as the very first import.

    – Phix
    Nov 25 '18 at 17:42











  • Thanks :) It's the first thing I tried - it was on top of the polyfills file before. Sadly, no difference :(

    – Rumen Rumenov
    Nov 25 '18 at 18:14











  • What the context of the original throw: ...clientssitelayoutindex.js:83:9 ?

    – Phix
    Nov 25 '18 at 18:22











  • It's the rendering line from my server setup snippet - res.render(path.join('../../../../public/site/layout.html'), {req, res})

    – Rumen Rumenov
    Nov 25 '18 at 19:24











  • Can you provide a minimal reproduction on github?

    – yurzui
    Nov 26 '18 at 9:15














8












8








8








I've recently moved my company's website from React to Angular, since most our projects were already on Angular 7. Being the "use-the-latest-and-greatest" person that I am, I decided to implement server-side rendering to get the google page speed rating close to that 100/100 (currently 42/100). I've been tinkering with it for the better part of the week now, unsuccessfully - the latest roadblock has been especially hard for me to overcome. Here's a brief info about my setup, then I'll get into the details:




  • NodeJS 8.9.1

  • Angular 7 latest

  • Webpack 4.26.0

  • @ngtools/webpack 7.0.5

  • Not using angular-cli

  • AoT setup

  • single page app


This is the error I'm getting when I try to render the layout.html file that's set up for SSR:



TypeError: Cannot read property 'subscribe' of undefined
at new ApplicationRef (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:43263:37)
at _createClass (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46296:20)
at _createProviderInstance (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46258:26)
at initNgModule (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46190:32)
at new NgModuleRef_ (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46918:9)
at createNgModuleRef (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46907:12)
at Object.debugCreateNgModuleRef [as createNgModuleRef] (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:48738:12)
at NgModuleFactory_.module.exports.NgModuleFactory_.create (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:49466:25)
at C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14656:47
at ZoneDelegate.module.exports.ZoneDelegate.invoke (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:139510:26)
at Object.onInvoke (C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14194:37)
at ZoneDelegate.module.exports.ZoneDelegate.invoke (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:139509:32)
at Zone.module.exports.Zone.run (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:139260:43)
at NgZone.run (C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14108:32)
at PlatformRef.bootstrapModuleFactory (C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14654:27)
at renderModuleFactory (C:codelemmsoftWebsitereponode_modules@angularplatform-serverbundlesplatform-server.umd.js:1033:43)
at View.module.app.engine (C:codelemmsoftWebsiterepomodulesclientssitelayoutindex.js:60:4)
at View.render (C:codelemmsoftWebsitereponode_modulesexpresslibview.js:135:8)
at tryRender (C:codelemmsoftWebsitereponode_modulesexpresslibapplication.js:640:10)
at Function.render (C:codelemmsoftWebsitereponode_modulesexpresslibapplication.js:592:3)
at ServerResponse.render (C:codelemmsoftWebsitereponode_modulesexpresslibresponse.js:1008:7)
at C:codelemmsoftWebsiterepomodulesclientssitelayoutindex.js:83:9


After much reading through the main.js bundle file, I've pinpointed the problem to the following:






var ApplicationRef = /** @class */ (function () {
/** @internal */
function ApplicationRef(_zone, _console, _injector, _exceptionHandler, _componentFactoryResolver, _initStatus) {
var _this = this;
this._zone = _zone; // in this method, the _zone argument is {}, so there is no onMicrotaskEmpty method in it => when this._zone.onMicrotaskEmpty.subscribe() is attempted, we get "Cannot read property 'subscribe' of undefined"
this._console = _console;
this._injector = _injector;
this._exceptionHandler = _exceptionHandler;
this._componentFactoryResolver = _componentFactoryResolver;
this._initStatus = _initStatus;
this._bootstrapListeners = ;
this._views = ;
this._runningTick = false;
this._enforceNoNewChanges = false;
this._stable = true;
// more code
}
// more code
}





In this method, the _zone argument is {}, so there is no onMicrotaskEmpty method in it => when this._zone.onMicrotaskEmpty.subscribe() is attempted, we get "Cannot read property 'subscribe' of undefined". I kept on digging through the stack trace - this the previous step, where new ApplicationRef is invoked and _zone is passed as {}:






function _createClass(ngModule, ctor, deps) {
var len = deps.length;
switch (len) {
case 0:
return new ctor();
case 1:
return new ctor(resolveNgModuleDep(ngModule, deps[0]));
case 2:
return new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]));
case 3:
return new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]), resolveNgModuleDep(ngModule, deps[2]));
default:
// this is where we get some insight into the cause of the error
var depValues = new Array(len);
for (var i = 0; i < len; i++) {
depValues[i] = resolveNgModuleDep(ngModule, deps[i]);
}
// if we do console.log(deps[0], depValues[0]), which is _zone, we get interesting stuff...
return new (ctor.bind.apply(ctor, Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__spread"])([void 0], depValues)))();
}
}





This is where we get some insight into the cause of the error - in the 'default' block of the switch. If we console.log(deps[0], depValues[0]) after the for loop, which is _zone, we get interesting stuff:






// deps[0]
{ flags: 0,
token:
{ [Function: NgZone]
isInAngularZone: [Function],
assertInAngularZone: [Function],
assertNotInAngularZone: [Function] },
tokenKey: 'NgZone_29' }
// depValues[0]
{}





So, here's the culprit, I thought! 'resolveNgModuleDep' screws it up! So I kept on digging:






function resolveNgModuleDep(data, depDef, notFoundValue) {
if (notFoundValue === void 0) { notFoundValue = Injector.THROW_IF_NOT_FOUND; }
var former = setCurrentInjector(data);
try {
if (depDef.flags & 8 /* Value */) {
return depDef.token;
}
if (depDef.flags & 2 /* Optional */) {
notFoundValue = null;
}
if (depDef.flags & 1 /* SkipSelf */) {
return data._parent.get(depDef.token, notFoundValue);
}
var tokenKey_1 = depDef.tokenKey;
switch (tokenKey_1) {
case InjectorRefTokenKey:
case INJECTORRefTokenKey:
case NgModuleRefTokenKey:
return data;
}
var providerDef = data._def.providersByKey[tokenKey_1];
var injectableDef = void 0;
if (providerDef) {
var providerInstance = data._providers[providerDef.index];
if (providerInstance === undefined) {
providerInstance = data._providers[providerDef.index] =
_createProviderInstance(data, providerDef);
}
return providerInstance === UNDEFINED_VALUE ? undefined : providerInstance;
}
else if ((injectableDef = getInjectableDef(depDef.token)) && targetsModule(data, injectableDef)) {
var index = data._providers.length;
data._def.providersByKey[depDef.tokenKey] = {
flags: 1024 /* TypeFactoryProvider */ | 4096 /* LazyProvider */,
value: injectableDef.factory,
deps: , index: index,
token: depDef.token,
};
data._providers[index] = UNDEFINED_VALUE;
return (data._providers[index] =
_createProviderInstance(data, data._def.providersByKey[depDef.tokenKey]));
}
else if (depDef.flags & 4 /* Self */) {
return notFoundValue;
}
// there it is!
return data._parent.get(depDef.token, notFoundValue);
}
finally {
setCurrentInjector(former);
}
}





There it is! Right before finally, on the line that returns data._parent.get(depDef.token, notFoundValue) - this is where depDef.token (which in our case is NgZone) is passed, notFoundValue is null. The returned object is just {}, hence all the troubles later on.
This is as far as I've managed to get, I've been going back and forth trying to solve it from here, but to no avail. Believe me, I've searched through and through in stackoverflow and just in google; I've read a 1000 medium posts - no success. I don't use angular-cli because I like to customize my webpack config, among other things, but I doubt that's the reason, because angular-cli itself uses webpack under the hood. I'm going to paste a few additional stuff below in several snippets - my webpack config, the server method where the html and angular bundle is rendered, etc.






// the webpack config

'use strict'

const
AngularCompilerPlugin = require( "@ngtools/webpack" ).AngularCompilerPlugin,
BellOnBundlerErrorPlugin = require('bell-on-bundler-error-plugin'),
BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin,
path = require('path'),
ProgressBarPlugin = require('progress-bar-webpack-plugin'),
UglifyJsPlugin = require('uglifyjs-webpack-plugin'),
webpack = require('webpack')


module.exports = (config, name) => {
let includePath = config.clientPath,
publicPath = path.join(config.publicPath, 'serverBuild'),
libPath = path.join(__dirname, '../../lib'),
nodeModulesPath = config.nodeModulesPath,
include = [includePath]

return {
target: 'node',
mode: 'none',
entry: [
path.join(includePath, 'polyfills.ts'),
path.join(includePath, 'vendor.common.ts'),
path.join(includePath, 'vendor.server.ts'),
path.join(includePath, 'index.server.ts')
],
output: {
path: publicPath,
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '/dist/',
libraryTarget: 'commonjs-module'
},
resolve: {
extensions: ['.ts', '.js'],
modules: ['node_modules', libPath]
},
module: {
rules: [
{
test: /.pug$/,
include: [libPath, includePath],
use: ['raw-loader', 'pug-html-loader']
},
{
test: /.css$/,
include: [libPath, nodeModulesPath, includePath],
exclude: ,
use: ['to-string-loader', 'css-loader']
},
{
test: /.less$/,
exclude: ,
use: ['to-string-loader', 'css-loader', 'less-loader']
},
{
test: /.scss$/,
include: [libPath, nodeModulesPath, includePath],
exclude: ,
use: ['raw-loader', 'sass-loader']
},
{
test: /(?:.ngfactory.js|.ngstyle.js|.ts)$/,
include: [includePath, libPath],
use: [{
loader: '@ngtools/webpack'
}],
exclude: [/.(spec|e2e).ts$/]
},
{
test: /.json$/,
include,
exclude: ,
use: ["json2-loader"]
}
]
},
stats: 'verbose',
plugins: [
// new webpack.HotModuleReplacementPlugin(),
new BellOnBundlerErrorPlugin(),
new ProgressBarPlugin({
format: ' build [:bar] (:percent) - (:elapsed seconds)',
clear: false,
complete: '#',
summary: 'true'
}),
// new webpack.NamedModulesPlugin(),
new AngularCompilerPlugin({
tsConfigPath: path.join(__dirname, '../../tsconfig.server.json'),
entryModule: path.join(includePath, 'app.server.ts#AppServerModule'),
sourceMap: true
})
]
}
}








// tsconfig.json and tsconfig.server.json
{
"compilerOptions": {
"baseUrl": ".",
"target": "es6",
"module": "es2015",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"importHelpers": true,
"strictNullChecks": false,
"lib": [
"es2015",
"dom"
],
"typeRoots": [
"node_modules/@types",
"typings"
],
"types": [
"hammerjs",
"node"
],
"paths": {
"ramster-ui/*": ["lib/ramster-ui/*"]
}
},
"include": [
"clients/**/*",
"lib/ramster-ui/**/*"
],
"exclude": [
"clients/**/*.spec.ts",
"clients/**/*.e2e.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}


{
"extends": "./tsconfig.json",
"include": [
"clients/**/polyfills.ts",
"clients/**/vendor.common.ts",
"clients/**/vendor.server.ts",
"clients/**/app.server.ts",
"clients/**/index.server.ts",
],
"exclude": [
"clients/**/index.ts",
"clients/**/vendor.browser.ts",
"clients/**/app.ts",
"clients/**/*.spec.ts",
"clients/**/*.e2e.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}








// excerpts from my server setup

// this method is called before the server is started
setup() {
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require(path.join(__dirname, '../../../../public/site/serverBuild', 'main'))
// LAZY_MODULE_MAP is undefined for now
this.module.app.engine('html', (_, options, callback) => {
renderModuleFactory(
AppServerModuleNgFactory, {}
).then((html) => callback(null, html), (error) => callback(error))
})
this.module.app.set('view engine', 'html')
}

// the method returned by "loadLayout" is mounted in expressjs
loadLayout() {
const {module} = this
return function* (req, res, next) {
try {
res.render(path.join('../../../../public/site/layout.html'), {req, res})
} catch (e) {
req.locals.error = e
next()
}
}
}








// polyfills.ts
import 'core-js/es6'
import 'reflect-metadata'


// vendor.common.ts
import 'rxjs/add/operator/first'
import 'rxjs/add/operator/toPromise'
import 'popper.js'

import '@angular/common'
import '@angular/core'
import '@angular/flex-layout'
import '@angular/forms'
import '@angular/material'
import '@angular/router'


// vendor.server.ts
import 'zone.js/dist/zone-node'
import '@angular/platform-server'


// index.server.ts
import {enableProdMode} from '@angular/core'
if (process.env.NODE_ENV === 'production') {
enableProdMode()
}
export * from './app.server.ngfactory'


// app.server.ts
import {NgModule} from '@angular/core'
import {ServerModule} from '@angular/platform-server'

@NgModule({
imports: [
ServerModule
],
exports: ,
declarations: ,
providers: ,
bootstrap:
})
class AppServerModule {}

export {AppServerModule}





You'll see that I've trimmed down the server-side app to the very basics, so I can eliminate the error being caused by something I've written.
Any help would be MUCH appreciated.










share|improve this question
















I've recently moved my company's website from React to Angular, since most our projects were already on Angular 7. Being the "use-the-latest-and-greatest" person that I am, I decided to implement server-side rendering to get the google page speed rating close to that 100/100 (currently 42/100). I've been tinkering with it for the better part of the week now, unsuccessfully - the latest roadblock has been especially hard for me to overcome. Here's a brief info about my setup, then I'll get into the details:




  • NodeJS 8.9.1

  • Angular 7 latest

  • Webpack 4.26.0

  • @ngtools/webpack 7.0.5

  • Not using angular-cli

  • AoT setup

  • single page app


This is the error I'm getting when I try to render the layout.html file that's set up for SSR:



TypeError: Cannot read property 'subscribe' of undefined
at new ApplicationRef (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:43263:37)
at _createClass (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46296:20)
at _createProviderInstance (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46258:26)
at initNgModule (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46190:32)
at new NgModuleRef_ (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46918:9)
at createNgModuleRef (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:46907:12)
at Object.debugCreateNgModuleRef [as createNgModuleRef] (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:48738:12)
at NgModuleFactory_.module.exports.NgModuleFactory_.create (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:49466:25)
at C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14656:47
at ZoneDelegate.module.exports.ZoneDelegate.invoke (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:139510:26)
at Object.onInvoke (C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14194:37)
at ZoneDelegate.module.exports.ZoneDelegate.invoke (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:139509:32)
at Zone.module.exports.Zone.run (C:codelemmsoftWebsiterepopublicsiteserverBuildmain.js:139260:43)
at NgZone.run (C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14108:32)
at PlatformRef.bootstrapModuleFactory (C:codelemmsoftWebsitereponode_modules@angularcorebundlescore.umd.js:14654:27)
at renderModuleFactory (C:codelemmsoftWebsitereponode_modules@angularplatform-serverbundlesplatform-server.umd.js:1033:43)
at View.module.app.engine (C:codelemmsoftWebsiterepomodulesclientssitelayoutindex.js:60:4)
at View.render (C:codelemmsoftWebsitereponode_modulesexpresslibview.js:135:8)
at tryRender (C:codelemmsoftWebsitereponode_modulesexpresslibapplication.js:640:10)
at Function.render (C:codelemmsoftWebsitereponode_modulesexpresslibapplication.js:592:3)
at ServerResponse.render (C:codelemmsoftWebsitereponode_modulesexpresslibresponse.js:1008:7)
at C:codelemmsoftWebsiterepomodulesclientssitelayoutindex.js:83:9


After much reading through the main.js bundle file, I've pinpointed the problem to the following:






var ApplicationRef = /** @class */ (function () {
/** @internal */
function ApplicationRef(_zone, _console, _injector, _exceptionHandler, _componentFactoryResolver, _initStatus) {
var _this = this;
this._zone = _zone; // in this method, the _zone argument is {}, so there is no onMicrotaskEmpty method in it => when this._zone.onMicrotaskEmpty.subscribe() is attempted, we get "Cannot read property 'subscribe' of undefined"
this._console = _console;
this._injector = _injector;
this._exceptionHandler = _exceptionHandler;
this._componentFactoryResolver = _componentFactoryResolver;
this._initStatus = _initStatus;
this._bootstrapListeners = ;
this._views = ;
this._runningTick = false;
this._enforceNoNewChanges = false;
this._stable = true;
// more code
}
// more code
}





In this method, the _zone argument is {}, so there is no onMicrotaskEmpty method in it => when this._zone.onMicrotaskEmpty.subscribe() is attempted, we get "Cannot read property 'subscribe' of undefined". I kept on digging through the stack trace - this the previous step, where new ApplicationRef is invoked and _zone is passed as {}:






function _createClass(ngModule, ctor, deps) {
var len = deps.length;
switch (len) {
case 0:
return new ctor();
case 1:
return new ctor(resolveNgModuleDep(ngModule, deps[0]));
case 2:
return new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]));
case 3:
return new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]), resolveNgModuleDep(ngModule, deps[2]));
default:
// this is where we get some insight into the cause of the error
var depValues = new Array(len);
for (var i = 0; i < len; i++) {
depValues[i] = resolveNgModuleDep(ngModule, deps[i]);
}
// if we do console.log(deps[0], depValues[0]), which is _zone, we get interesting stuff...
return new (ctor.bind.apply(ctor, Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__spread"])([void 0], depValues)))();
}
}





This is where we get some insight into the cause of the error - in the 'default' block of the switch. If we console.log(deps[0], depValues[0]) after the for loop, which is _zone, we get interesting stuff:






// deps[0]
{ flags: 0,
token:
{ [Function: NgZone]
isInAngularZone: [Function],
assertInAngularZone: [Function],
assertNotInAngularZone: [Function] },
tokenKey: 'NgZone_29' }
// depValues[0]
{}





So, here's the culprit, I thought! 'resolveNgModuleDep' screws it up! So I kept on digging:






function resolveNgModuleDep(data, depDef, notFoundValue) {
if (notFoundValue === void 0) { notFoundValue = Injector.THROW_IF_NOT_FOUND; }
var former = setCurrentInjector(data);
try {
if (depDef.flags & 8 /* Value */) {
return depDef.token;
}
if (depDef.flags & 2 /* Optional */) {
notFoundValue = null;
}
if (depDef.flags & 1 /* SkipSelf */) {
return data._parent.get(depDef.token, notFoundValue);
}
var tokenKey_1 = depDef.tokenKey;
switch (tokenKey_1) {
case InjectorRefTokenKey:
case INJECTORRefTokenKey:
case NgModuleRefTokenKey:
return data;
}
var providerDef = data._def.providersByKey[tokenKey_1];
var injectableDef = void 0;
if (providerDef) {
var providerInstance = data._providers[providerDef.index];
if (providerInstance === undefined) {
providerInstance = data._providers[providerDef.index] =
_createProviderInstance(data, providerDef);
}
return providerInstance === UNDEFINED_VALUE ? undefined : providerInstance;
}
else if ((injectableDef = getInjectableDef(depDef.token)) && targetsModule(data, injectableDef)) {
var index = data._providers.length;
data._def.providersByKey[depDef.tokenKey] = {
flags: 1024 /* TypeFactoryProvider */ | 4096 /* LazyProvider */,
value: injectableDef.factory,
deps: , index: index,
token: depDef.token,
};
data._providers[index] = UNDEFINED_VALUE;
return (data._providers[index] =
_createProviderInstance(data, data._def.providersByKey[depDef.tokenKey]));
}
else if (depDef.flags & 4 /* Self */) {
return notFoundValue;
}
// there it is!
return data._parent.get(depDef.token, notFoundValue);
}
finally {
setCurrentInjector(former);
}
}





There it is! Right before finally, on the line that returns data._parent.get(depDef.token, notFoundValue) - this is where depDef.token (which in our case is NgZone) is passed, notFoundValue is null. The returned object is just {}, hence all the troubles later on.
This is as far as I've managed to get, I've been going back and forth trying to solve it from here, but to no avail. Believe me, I've searched through and through in stackoverflow and just in google; I've read a 1000 medium posts - no success. I don't use angular-cli because I like to customize my webpack config, among other things, but I doubt that's the reason, because angular-cli itself uses webpack under the hood. I'm going to paste a few additional stuff below in several snippets - my webpack config, the server method where the html and angular bundle is rendered, etc.






// the webpack config

'use strict'

const
AngularCompilerPlugin = require( "@ngtools/webpack" ).AngularCompilerPlugin,
BellOnBundlerErrorPlugin = require('bell-on-bundler-error-plugin'),
BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin,
path = require('path'),
ProgressBarPlugin = require('progress-bar-webpack-plugin'),
UglifyJsPlugin = require('uglifyjs-webpack-plugin'),
webpack = require('webpack')


module.exports = (config, name) => {
let includePath = config.clientPath,
publicPath = path.join(config.publicPath, 'serverBuild'),
libPath = path.join(__dirname, '../../lib'),
nodeModulesPath = config.nodeModulesPath,
include = [includePath]

return {
target: 'node',
mode: 'none',
entry: [
path.join(includePath, 'polyfills.ts'),
path.join(includePath, 'vendor.common.ts'),
path.join(includePath, 'vendor.server.ts'),
path.join(includePath, 'index.server.ts')
],
output: {
path: publicPath,
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '/dist/',
libraryTarget: 'commonjs-module'
},
resolve: {
extensions: ['.ts', '.js'],
modules: ['node_modules', libPath]
},
module: {
rules: [
{
test: /.pug$/,
include: [libPath, includePath],
use: ['raw-loader', 'pug-html-loader']
},
{
test: /.css$/,
include: [libPath, nodeModulesPath, includePath],
exclude: ,
use: ['to-string-loader', 'css-loader']
},
{
test: /.less$/,
exclude: ,
use: ['to-string-loader', 'css-loader', 'less-loader']
},
{
test: /.scss$/,
include: [libPath, nodeModulesPath, includePath],
exclude: ,
use: ['raw-loader', 'sass-loader']
},
{
test: /(?:.ngfactory.js|.ngstyle.js|.ts)$/,
include: [includePath, libPath],
use: [{
loader: '@ngtools/webpack'
}],
exclude: [/.(spec|e2e).ts$/]
},
{
test: /.json$/,
include,
exclude: ,
use: ["json2-loader"]
}
]
},
stats: 'verbose',
plugins: [
// new webpack.HotModuleReplacementPlugin(),
new BellOnBundlerErrorPlugin(),
new ProgressBarPlugin({
format: ' build [:bar] (:percent) - (:elapsed seconds)',
clear: false,
complete: '#',
summary: 'true'
}),
// new webpack.NamedModulesPlugin(),
new AngularCompilerPlugin({
tsConfigPath: path.join(__dirname, '../../tsconfig.server.json'),
entryModule: path.join(includePath, 'app.server.ts#AppServerModule'),
sourceMap: true
})
]
}
}








// tsconfig.json and tsconfig.server.json
{
"compilerOptions": {
"baseUrl": ".",
"target": "es6",
"module": "es2015",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"importHelpers": true,
"strictNullChecks": false,
"lib": [
"es2015",
"dom"
],
"typeRoots": [
"node_modules/@types",
"typings"
],
"types": [
"hammerjs",
"node"
],
"paths": {
"ramster-ui/*": ["lib/ramster-ui/*"]
}
},
"include": [
"clients/**/*",
"lib/ramster-ui/**/*"
],
"exclude": [
"clients/**/*.spec.ts",
"clients/**/*.e2e.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}


{
"extends": "./tsconfig.json",
"include": [
"clients/**/polyfills.ts",
"clients/**/vendor.common.ts",
"clients/**/vendor.server.ts",
"clients/**/app.server.ts",
"clients/**/index.server.ts",
],
"exclude": [
"clients/**/index.ts",
"clients/**/vendor.browser.ts",
"clients/**/app.ts",
"clients/**/*.spec.ts",
"clients/**/*.e2e.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}








// excerpts from my server setup

// this method is called before the server is started
setup() {
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require(path.join(__dirname, '../../../../public/site/serverBuild', 'main'))
// LAZY_MODULE_MAP is undefined for now
this.module.app.engine('html', (_, options, callback) => {
renderModuleFactory(
AppServerModuleNgFactory, {}
).then((html) => callback(null, html), (error) => callback(error))
})
this.module.app.set('view engine', 'html')
}

// the method returned by "loadLayout" is mounted in expressjs
loadLayout() {
const {module} = this
return function* (req, res, next) {
try {
res.render(path.join('../../../../public/site/layout.html'), {req, res})
} catch (e) {
req.locals.error = e
next()
}
}
}








// polyfills.ts
import 'core-js/es6'
import 'reflect-metadata'


// vendor.common.ts
import 'rxjs/add/operator/first'
import 'rxjs/add/operator/toPromise'
import 'popper.js'

import '@angular/common'
import '@angular/core'
import '@angular/flex-layout'
import '@angular/forms'
import '@angular/material'
import '@angular/router'


// vendor.server.ts
import 'zone.js/dist/zone-node'
import '@angular/platform-server'


// index.server.ts
import {enableProdMode} from '@angular/core'
if (process.env.NODE_ENV === 'production') {
enableProdMode()
}
export * from './app.server.ngfactory'


// app.server.ts
import {NgModule} from '@angular/core'
import {ServerModule} from '@angular/platform-server'

@NgModule({
imports: [
ServerModule
],
exports: ,
declarations: ,
providers: ,
bootstrap:
})
class AppServerModule {}

export {AppServerModule}





You'll see that I've trimmed down the server-side app to the very basics, so I can eliminate the error being caused by something I've written.
Any help would be MUCH appreciated.






var ApplicationRef = /** @class */ (function () {
/** @internal */
function ApplicationRef(_zone, _console, _injector, _exceptionHandler, _componentFactoryResolver, _initStatus) {
var _this = this;
this._zone = _zone; // in this method, the _zone argument is {}, so there is no onMicrotaskEmpty method in it => when this._zone.onMicrotaskEmpty.subscribe() is attempted, we get "Cannot read property 'subscribe' of undefined"
this._console = _console;
this._injector = _injector;
this._exceptionHandler = _exceptionHandler;
this._componentFactoryResolver = _componentFactoryResolver;
this._initStatus = _initStatus;
this._bootstrapListeners = ;
this._views = ;
this._runningTick = false;
this._enforceNoNewChanges = false;
this._stable = true;
// more code
}
// more code
}





var ApplicationRef = /** @class */ (function () {
/** @internal */
function ApplicationRef(_zone, _console, _injector, _exceptionHandler, _componentFactoryResolver, _initStatus) {
var _this = this;
this._zone = _zone; // in this method, the _zone argument is {}, so there is no onMicrotaskEmpty method in it => when this._zone.onMicrotaskEmpty.subscribe() is attempted, we get "Cannot read property 'subscribe' of undefined"
this._console = _console;
this._injector = _injector;
this._exceptionHandler = _exceptionHandler;
this._componentFactoryResolver = _componentFactoryResolver;
this._initStatus = _initStatus;
this._bootstrapListeners = ;
this._views = ;
this._runningTick = false;
this._enforceNoNewChanges = false;
this._stable = true;
// more code
}
// more code
}





function _createClass(ngModule, ctor, deps) {
var len = deps.length;
switch (len) {
case 0:
return new ctor();
case 1:
return new ctor(resolveNgModuleDep(ngModule, deps[0]));
case 2:
return new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]));
case 3:
return new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]), resolveNgModuleDep(ngModule, deps[2]));
default:
// this is where we get some insight into the cause of the error
var depValues = new Array(len);
for (var i = 0; i < len; i++) {
depValues[i] = resolveNgModuleDep(ngModule, deps[i]);
}
// if we do console.log(deps[0], depValues[0]), which is _zone, we get interesting stuff...
return new (ctor.bind.apply(ctor, Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__spread"])([void 0], depValues)))();
}
}





function _createClass(ngModule, ctor, deps) {
var len = deps.length;
switch (len) {
case 0:
return new ctor();
case 1:
return new ctor(resolveNgModuleDep(ngModule, deps[0]));
case 2:
return new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]));
case 3:
return new ctor(resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]), resolveNgModuleDep(ngModule, deps[2]));
default:
// this is where we get some insight into the cause of the error
var depValues = new Array(len);
for (var i = 0; i < len; i++) {
depValues[i] = resolveNgModuleDep(ngModule, deps[i]);
}
// if we do console.log(deps[0], depValues[0]), which is _zone, we get interesting stuff...
return new (ctor.bind.apply(ctor, Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__spread"])([void 0], depValues)))();
}
}





// deps[0]
{ flags: 0,
token:
{ [Function: NgZone]
isInAngularZone: [Function],
assertInAngularZone: [Function],
assertNotInAngularZone: [Function] },
tokenKey: 'NgZone_29' }
// depValues[0]
{}





// deps[0]
{ flags: 0,
token:
{ [Function: NgZone]
isInAngularZone: [Function],
assertInAngularZone: [Function],
assertNotInAngularZone: [Function] },
tokenKey: 'NgZone_29' }
// depValues[0]
{}





function resolveNgModuleDep(data, depDef, notFoundValue) {
if (notFoundValue === void 0) { notFoundValue = Injector.THROW_IF_NOT_FOUND; }
var former = setCurrentInjector(data);
try {
if (depDef.flags & 8 /* Value */) {
return depDef.token;
}
if (depDef.flags & 2 /* Optional */) {
notFoundValue = null;
}
if (depDef.flags & 1 /* SkipSelf */) {
return data._parent.get(depDef.token, notFoundValue);
}
var tokenKey_1 = depDef.tokenKey;
switch (tokenKey_1) {
case InjectorRefTokenKey:
case INJECTORRefTokenKey:
case NgModuleRefTokenKey:
return data;
}
var providerDef = data._def.providersByKey[tokenKey_1];
var injectableDef = void 0;
if (providerDef) {
var providerInstance = data._providers[providerDef.index];
if (providerInstance === undefined) {
providerInstance = data._providers[providerDef.index] =
_createProviderInstance(data, providerDef);
}
return providerInstance === UNDEFINED_VALUE ? undefined : providerInstance;
}
else if ((injectableDef = getInjectableDef(depDef.token)) && targetsModule(data, injectableDef)) {
var index = data._providers.length;
data._def.providersByKey[depDef.tokenKey] = {
flags: 1024 /* TypeFactoryProvider */ | 4096 /* LazyProvider */,
value: injectableDef.factory,
deps: , index: index,
token: depDef.token,
};
data._providers[index] = UNDEFINED_VALUE;
return (data._providers[index] =
_createProviderInstance(data, data._def.providersByKey[depDef.tokenKey]));
}
else if (depDef.flags & 4 /* Self */) {
return notFoundValue;
}
// there it is!
return data._parent.get(depDef.token, notFoundValue);
}
finally {
setCurrentInjector(former);
}
}





function resolveNgModuleDep(data, depDef, notFoundValue) {
if (notFoundValue === void 0) { notFoundValue = Injector.THROW_IF_NOT_FOUND; }
var former = setCurrentInjector(data);
try {
if (depDef.flags & 8 /* Value */) {
return depDef.token;
}
if (depDef.flags & 2 /* Optional */) {
notFoundValue = null;
}
if (depDef.flags & 1 /* SkipSelf */) {
return data._parent.get(depDef.token, notFoundValue);
}
var tokenKey_1 = depDef.tokenKey;
switch (tokenKey_1) {
case InjectorRefTokenKey:
case INJECTORRefTokenKey:
case NgModuleRefTokenKey:
return data;
}
var providerDef = data._def.providersByKey[tokenKey_1];
var injectableDef = void 0;
if (providerDef) {
var providerInstance = data._providers[providerDef.index];
if (providerInstance === undefined) {
providerInstance = data._providers[providerDef.index] =
_createProviderInstance(data, providerDef);
}
return providerInstance === UNDEFINED_VALUE ? undefined : providerInstance;
}
else if ((injectableDef = getInjectableDef(depDef.token)) && targetsModule(data, injectableDef)) {
var index = data._providers.length;
data._def.providersByKey[depDef.tokenKey] = {
flags: 1024 /* TypeFactoryProvider */ | 4096 /* LazyProvider */,
value: injectableDef.factory,
deps: , index: index,
token: depDef.token,
};
data._providers[index] = UNDEFINED_VALUE;
return (data._providers[index] =
_createProviderInstance(data, data._def.providersByKey[depDef.tokenKey]));
}
else if (depDef.flags & 4 /* Self */) {
return notFoundValue;
}
// there it is!
return data._parent.get(depDef.token, notFoundValue);
}
finally {
setCurrentInjector(former);
}
}





// the webpack config

'use strict'

const
AngularCompilerPlugin = require( "@ngtools/webpack" ).AngularCompilerPlugin,
BellOnBundlerErrorPlugin = require('bell-on-bundler-error-plugin'),
BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin,
path = require('path'),
ProgressBarPlugin = require('progress-bar-webpack-plugin'),
UglifyJsPlugin = require('uglifyjs-webpack-plugin'),
webpack = require('webpack')


module.exports = (config, name) => {
let includePath = config.clientPath,
publicPath = path.join(config.publicPath, 'serverBuild'),
libPath = path.join(__dirname, '../../lib'),
nodeModulesPath = config.nodeModulesPath,
include = [includePath]

return {
target: 'node',
mode: 'none',
entry: [
path.join(includePath, 'polyfills.ts'),
path.join(includePath, 'vendor.common.ts'),
path.join(includePath, 'vendor.server.ts'),
path.join(includePath, 'index.server.ts')
],
output: {
path: publicPath,
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '/dist/',
libraryTarget: 'commonjs-module'
},
resolve: {
extensions: ['.ts', '.js'],
modules: ['node_modules', libPath]
},
module: {
rules: [
{
test: /.pug$/,
include: [libPath, includePath],
use: ['raw-loader', 'pug-html-loader']
},
{
test: /.css$/,
include: [libPath, nodeModulesPath, includePath],
exclude: ,
use: ['to-string-loader', 'css-loader']
},
{
test: /.less$/,
exclude: ,
use: ['to-string-loader', 'css-loader', 'less-loader']
},
{
test: /.scss$/,
include: [libPath, nodeModulesPath, includePath],
exclude: ,
use: ['raw-loader', 'sass-loader']
},
{
test: /(?:.ngfactory.js|.ngstyle.js|.ts)$/,
include: [includePath, libPath],
use: [{
loader: '@ngtools/webpack'
}],
exclude: [/.(spec|e2e).ts$/]
},
{
test: /.json$/,
include,
exclude: ,
use: ["json2-loader"]
}
]
},
stats: 'verbose',
plugins: [
// new webpack.HotModuleReplacementPlugin(),
new BellOnBundlerErrorPlugin(),
new ProgressBarPlugin({
format: ' build [:bar] (:percent) - (:elapsed seconds)',
clear: false,
complete: '#',
summary: 'true'
}),
// new webpack.NamedModulesPlugin(),
new AngularCompilerPlugin({
tsConfigPath: path.join(__dirname, '../../tsconfig.server.json'),
entryModule: path.join(includePath, 'app.server.ts#AppServerModule'),
sourceMap: true
})
]
}
}





// the webpack config

'use strict'

const
AngularCompilerPlugin = require( "@ngtools/webpack" ).AngularCompilerPlugin,
BellOnBundlerErrorPlugin = require('bell-on-bundler-error-plugin'),
BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin,
path = require('path'),
ProgressBarPlugin = require('progress-bar-webpack-plugin'),
UglifyJsPlugin = require('uglifyjs-webpack-plugin'),
webpack = require('webpack')


module.exports = (config, name) => {
let includePath = config.clientPath,
publicPath = path.join(config.publicPath, 'serverBuild'),
libPath = path.join(__dirname, '../../lib'),
nodeModulesPath = config.nodeModulesPath,
include = [includePath]

return {
target: 'node',
mode: 'none',
entry: [
path.join(includePath, 'polyfills.ts'),
path.join(includePath, 'vendor.common.ts'),
path.join(includePath, 'vendor.server.ts'),
path.join(includePath, 'index.server.ts')
],
output: {
path: publicPath,
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '/dist/',
libraryTarget: 'commonjs-module'
},
resolve: {
extensions: ['.ts', '.js'],
modules: ['node_modules', libPath]
},
module: {
rules: [
{
test: /.pug$/,
include: [libPath, includePath],
use: ['raw-loader', 'pug-html-loader']
},
{
test: /.css$/,
include: [libPath, nodeModulesPath, includePath],
exclude: ,
use: ['to-string-loader', 'css-loader']
},
{
test: /.less$/,
exclude: ,
use: ['to-string-loader', 'css-loader', 'less-loader']
},
{
test: /.scss$/,
include: [libPath, nodeModulesPath, includePath],
exclude: ,
use: ['raw-loader', 'sass-loader']
},
{
test: /(?:.ngfactory.js|.ngstyle.js|.ts)$/,
include: [includePath, libPath],
use: [{
loader: '@ngtools/webpack'
}],
exclude: [/.(spec|e2e).ts$/]
},
{
test: /.json$/,
include,
exclude: ,
use: ["json2-loader"]
}
]
},
stats: 'verbose',
plugins: [
// new webpack.HotModuleReplacementPlugin(),
new BellOnBundlerErrorPlugin(),
new ProgressBarPlugin({
format: ' build [:bar] (:percent) - (:elapsed seconds)',
clear: false,
complete: '#',
summary: 'true'
}),
// new webpack.NamedModulesPlugin(),
new AngularCompilerPlugin({
tsConfigPath: path.join(__dirname, '../../tsconfig.server.json'),
entryModule: path.join(includePath, 'app.server.ts#AppServerModule'),
sourceMap: true
})
]
}
}





// tsconfig.json and tsconfig.server.json
{
"compilerOptions": {
"baseUrl": ".",
"target": "es6",
"module": "es2015",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"importHelpers": true,
"strictNullChecks": false,
"lib": [
"es2015",
"dom"
],
"typeRoots": [
"node_modules/@types",
"typings"
],
"types": [
"hammerjs",
"node"
],
"paths": {
"ramster-ui/*": ["lib/ramster-ui/*"]
}
},
"include": [
"clients/**/*",
"lib/ramster-ui/**/*"
],
"exclude": [
"clients/**/*.spec.ts",
"clients/**/*.e2e.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}


{
"extends": "./tsconfig.json",
"include": [
"clients/**/polyfills.ts",
"clients/**/vendor.common.ts",
"clients/**/vendor.server.ts",
"clients/**/app.server.ts",
"clients/**/index.server.ts",
],
"exclude": [
"clients/**/index.ts",
"clients/**/vendor.browser.ts",
"clients/**/app.ts",
"clients/**/*.spec.ts",
"clients/**/*.e2e.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}





// tsconfig.json and tsconfig.server.json
{
"compilerOptions": {
"baseUrl": ".",
"target": "es6",
"module": "es2015",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"importHelpers": true,
"strictNullChecks": false,
"lib": [
"es2015",
"dom"
],
"typeRoots": [
"node_modules/@types",
"typings"
],
"types": [
"hammerjs",
"node"
],
"paths": {
"ramster-ui/*": ["lib/ramster-ui/*"]
}
},
"include": [
"clients/**/*",
"lib/ramster-ui/**/*"
],
"exclude": [
"clients/**/*.spec.ts",
"clients/**/*.e2e.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}


{
"extends": "./tsconfig.json",
"include": [
"clients/**/polyfills.ts",
"clients/**/vendor.common.ts",
"clients/**/vendor.server.ts",
"clients/**/app.server.ts",
"clients/**/index.server.ts",
],
"exclude": [
"clients/**/index.ts",
"clients/**/vendor.browser.ts",
"clients/**/app.ts",
"clients/**/*.spec.ts",
"clients/**/*.e2e.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}





// excerpts from my server setup

// this method is called before the server is started
setup() {
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require(path.join(__dirname, '../../../../public/site/serverBuild', 'main'))
// LAZY_MODULE_MAP is undefined for now
this.module.app.engine('html', (_, options, callback) => {
renderModuleFactory(
AppServerModuleNgFactory, {}
).then((html) => callback(null, html), (error) => callback(error))
})
this.module.app.set('view engine', 'html')
}

// the method returned by "loadLayout" is mounted in expressjs
loadLayout() {
const {module} = this
return function* (req, res, next) {
try {
res.render(path.join('../../../../public/site/layout.html'), {req, res})
} catch (e) {
req.locals.error = e
next()
}
}
}





// excerpts from my server setup

// this method is called before the server is started
setup() {
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require(path.join(__dirname, '../../../../public/site/serverBuild', 'main'))
// LAZY_MODULE_MAP is undefined for now
this.module.app.engine('html', (_, options, callback) => {
renderModuleFactory(
AppServerModuleNgFactory, {}
).then((html) => callback(null, html), (error) => callback(error))
})
this.module.app.set('view engine', 'html')
}

// the method returned by "loadLayout" is mounted in expressjs
loadLayout() {
const {module} = this
return function* (req, res, next) {
try {
res.render(path.join('../../../../public/site/layout.html'), {req, res})
} catch (e) {
req.locals.error = e
next()
}
}
}





// polyfills.ts
import 'core-js/es6'
import 'reflect-metadata'


// vendor.common.ts
import 'rxjs/add/operator/first'
import 'rxjs/add/operator/toPromise'
import 'popper.js'

import '@angular/common'
import '@angular/core'
import '@angular/flex-layout'
import '@angular/forms'
import '@angular/material'
import '@angular/router'


// vendor.server.ts
import 'zone.js/dist/zone-node'
import '@angular/platform-server'


// index.server.ts
import {enableProdMode} from '@angular/core'
if (process.env.NODE_ENV === 'production') {
enableProdMode()
}
export * from './app.server.ngfactory'


// app.server.ts
import {NgModule} from '@angular/core'
import {ServerModule} from '@angular/platform-server'

@NgModule({
imports: [
ServerModule
],
exports: ,
declarations: ,
providers: ,
bootstrap:
})
class AppServerModule {}

export {AppServerModule}





// polyfills.ts
import 'core-js/es6'
import 'reflect-metadata'


// vendor.common.ts
import 'rxjs/add/operator/first'
import 'rxjs/add/operator/toPromise'
import 'popper.js'

import '@angular/common'
import '@angular/core'
import '@angular/flex-layout'
import '@angular/forms'
import '@angular/material'
import '@angular/router'


// vendor.server.ts
import 'zone.js/dist/zone-node'
import '@angular/platform-server'


// index.server.ts
import {enableProdMode} from '@angular/core'
if (process.env.NODE_ENV === 'production') {
enableProdMode()
}
export * from './app.server.ngfactory'


// app.server.ts
import {NgModule} from '@angular/core'
import {ServerModule} from '@angular/platform-server'

@NgModule({
imports: [
ServerModule
],
exports: ,
declarations: ,
providers: ,
bootstrap:
})
class AppServerModule {}

export {AppServerModule}






angular webpack ssr zone.js ngzone






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 26 '18 at 17:33









E_net4

12.3k63669




12.3k63669










asked Nov 25 '18 at 9:58









Rumen RumenovRumen Rumenov

4112




4112













  • Just a shot in the dark, can you move import 'zone.js/dist/zone-node' to the top, as in include it as the very first import.

    – Phix
    Nov 25 '18 at 17:42











  • Thanks :) It's the first thing I tried - it was on top of the polyfills file before. Sadly, no difference :(

    – Rumen Rumenov
    Nov 25 '18 at 18:14











  • What the context of the original throw: ...clientssitelayoutindex.js:83:9 ?

    – Phix
    Nov 25 '18 at 18:22











  • It's the rendering line from my server setup snippet - res.render(path.join('../../../../public/site/layout.html'), {req, res})

    – Rumen Rumenov
    Nov 25 '18 at 19:24











  • Can you provide a minimal reproduction on github?

    – yurzui
    Nov 26 '18 at 9:15



















  • Just a shot in the dark, can you move import 'zone.js/dist/zone-node' to the top, as in include it as the very first import.

    – Phix
    Nov 25 '18 at 17:42











  • Thanks :) It's the first thing I tried - it was on top of the polyfills file before. Sadly, no difference :(

    – Rumen Rumenov
    Nov 25 '18 at 18:14











  • What the context of the original throw: ...clientssitelayoutindex.js:83:9 ?

    – Phix
    Nov 25 '18 at 18:22











  • It's the rendering line from my server setup snippet - res.render(path.join('../../../../public/site/layout.html'), {req, res})

    – Rumen Rumenov
    Nov 25 '18 at 19:24











  • Can you provide a minimal reproduction on github?

    – yurzui
    Nov 26 '18 at 9:15

















Just a shot in the dark, can you move import 'zone.js/dist/zone-node' to the top, as in include it as the very first import.

– Phix
Nov 25 '18 at 17:42





Just a shot in the dark, can you move import 'zone.js/dist/zone-node' to the top, as in include it as the very first import.

– Phix
Nov 25 '18 at 17:42













Thanks :) It's the first thing I tried - it was on top of the polyfills file before. Sadly, no difference :(

– Rumen Rumenov
Nov 25 '18 at 18:14





Thanks :) It's the first thing I tried - it was on top of the polyfills file before. Sadly, no difference :(

– Rumen Rumenov
Nov 25 '18 at 18:14













What the context of the original throw: ...clientssitelayoutindex.js:83:9 ?

– Phix
Nov 25 '18 at 18:22





What the context of the original throw: ...clientssitelayoutindex.js:83:9 ?

– Phix
Nov 25 '18 at 18:22













It's the rendering line from my server setup snippet - res.render(path.join('../../../../public/site/layout.html'), {req, res})

– Rumen Rumenov
Nov 25 '18 at 19:24





It's the rendering line from my server setup snippet - res.render(path.join('../../../../public/site/layout.html'), {req, res})

– Rumen Rumenov
Nov 25 '18 at 19:24













Can you provide a minimal reproduction on github?

– yurzui
Nov 26 '18 at 9:15





Can you provide a minimal reproduction on github?

– yurzui
Nov 26 '18 at 9:15












1 Answer
1






active

oldest

votes


















0














webpack must be mode: 'development',
If you can reproduce the problem with fork https://github.com/Angular-RU/angular-universal-starter - a can help.






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53466406%2fangular-7-ssr-problems-with-ngzone%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    webpack must be mode: 'development',
    If you can reproduce the problem with fork https://github.com/Angular-RU/angular-universal-starter - a can help.






    share|improve this answer




























      0














      webpack must be mode: 'development',
      If you can reproduce the problem with fork https://github.com/Angular-RU/angular-universal-starter - a can help.






      share|improve this answer


























        0












        0








        0







        webpack must be mode: 'development',
        If you can reproduce the problem with fork https://github.com/Angular-RU/angular-universal-starter - a can help.






        share|improve this answer













        webpack must be mode: 'development',
        If you can reproduce the problem with fork https://github.com/Angular-RU/angular-universal-starter - a can help.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Jan 14 at 7:52









        gornivgorniv

        366




        366
































            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53466406%2fangular-7-ssr-problems-with-ngzone%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            404 Error Contact Form 7 ajax form submitting

            How to know if a Active Directory user can login interactively

            Refactoring coordinates for Minecraft Pi buildings written in Python