本文介绍了Angular-universal 获取错误:您必须传入 NgModule 或 NgModuleFactory 才能引导的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我按照 将我现有的 angular-cli 应用程序转换为 angular-universal本指南.

您可以在此处查看我的完整源代码.

You can look at my complete source code here.

我能够同时构建浏览器和客户端项目,但在浏览器中查看应用程序时出现以下错误:

I am able to build both browser and client projects but I get following error when I view the app in the browser:

错误:您必须传入 NgModule 或 NgModuleFactory 才能成为自举在 View.engine (D:\ng-ssr-demo\dist\server.js:359545:23)

问题出在我的 server.ts 文件中,其中 AppServerModuleNgFactory 未定义,并且由于该工厂用于在快速后端引导应用程序,因此引导失败.

The issue is in my server.ts file where AppServerModuleNgFactory is being undefined and as this factory is used for bootstraping the app in the express backend, the bootstrapping is failing.

./server.ts:

const MockBrowser = require('mock-browser').mocks.MockBrowser;
const mock = new MockBrowser();

// 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(), 'dist');

// Fix for window error:
const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync(path.resolve('./', 'dist', 'browser/', 'index.html')).toString();
const win = domino.createWindow(template);

// workaround for leaflet
global['window'] = win;
global['document'] = win.document;

// workaround for nex-charts
win.screen = { deviceXDPI: 0, logicalXDPI: 0 };
global['MouseEvent'] = win.MouseEvent;
global['navigator'] = mock.getNavigator();


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

// AppServerModuleNgFactory is undefined
console.log('AppServerModuleNgFactory', AppServerModuleNgFactory);

// This is injected
console.log('LAZY_MODULE_MAP', LAZY_MODULE_MAP);

// 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)
  ]
}));

./webpack.server.config.js:

module.exports = {
  entry: {
    // This is our Express server for Dynamic universal
    server: './server.ts',
    // This is an example of Static prerendering (generative)
    prerender: './prerender.ts'
  },
  target: 'node',
  resolve: { extensions: ['.ts', '.js'] },
  // Make sure we include all node_modules etc
  externals: [/node_modules/],
  output: { path: path.join(__dirname, 'dist'), filename: '[name].js' },
  module: { rules: [{ test: /\.ts$/, loader: 'ts-loader'}] },
  plugins: [
    new webpack.ContextReplacementPlugin(
      // fixes WARNING Critical dependency: the request of a dependency is an expression
      /(.+)?angular(\\|\/)core(.+)?/,
      path.join(__dirname, 'src'), // location of your src
      {} // a map of your routes
    ),
    new webpack.ContextReplacementPlugin(
      // fixes WARNING Critical dependency: the request of a dependency is an expression
      /(.+)?express(\\|\/)(.+)?/,
      path.join(__dirname, 'src'), {}
    )
  ]
}

./src/tsconfig.server.json:

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "module": "commonjs",
    "baseUrl": "./",
    "types": ["node"],
    "typeRoots": ["../node_modules/@types"],
    "paths": {
      "@angular/*": [
        "../node_modules/@angular/*"
      ],
      "@nebular/*": [
        "../node_modules/@nebular/*"
      ]
    }
  },
  "exclude": [
    "test.ts",
    "**/*.spec.ts"
  ],
  "angularCompilerOptions": {
    "entryModule": "app/app.server.module#AppServerModule",
    "platform": 1
  }
}

./src/main.server.ts:

export { AppServerModule } from './app/app.server.module';

./src/app/app.module.ts:

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule.withServerTransition({appId: 'my-app'}),
    BrowserAnimationsModule,
    HttpModule,
    AppRoutingModule,

    NgbModule.forRoot(),
    ThemeModule.forRoot(),
    CoreModule.forRoot(),
    environment.production ? ServiceWorkerModule.register('./ngsw-worker.js') : [],
  ],
  bootstrap: [AppComponent],
  providers: [
    { provide: APP_BASE_HREF, useValue: '/' }, WebWorkerService,
  ],
})
export class AppModule {
}

./src/app/app.server.module.ts:

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

推荐答案

ANGULAR 8 更新(v8.0.1 - 截至 2019 年 6 月)

对我来说,我使用 Angular CLI 运行了通用设置,但它并没有直接运行.经过几个小时的阅读,我发现这些包裹不匹配.我让 Angular 8 运行该项目,但我的 package.json 中的 nguniversal 包是在 v7 中指定的.

For me, I ran the universal setup using the Angular CLI and it didn't work straight out the box. After hours of reading around it I found that the packages were miss-matched. I had Angular 8 running the project, but nguniversal packages in my package.json were specified at v7.

我建议将这些更新到您已安装的相同版本的 angular.默认情况下,CLI 确实应该这样做,但我猜不是(还没有?).

I would recommend updating these to the same version of angular you have installed. The CLI should really do this by default but I guess not (yet?).

对于 Angular 8,在撰写本文时(2019 年 6 月)这是版本 @next,或 @8.0.0-rc.1,因此运行以下命令更新命令:

For Angular 8, at the time of writing (June 2019) this is version @next, or @8.0.0-rc.1, so run the following command to update:

npm i --save @nguniversal/express-engine@next @nguniversal/module-map-ngfactory-loader@next

更新此内容后,我仍然收到错误消息,并设法确定了另一个问题.我还必须关闭服务器端应用程序的 Ivy 编译器.为此,我在 tsconfig.server.json 中添加了以下行:

After I updated this I was still getting the error, and managed to identify another issue. I also had to turn off the Ivy compiler for the server-side application. To do this I added the following line to the tsconfig.server.json:

{
    "extends": "./tsconfig.app.json",
    ...
    "angularCompilerOptions": {
        ...
        "enableIvy": false
    }
    ...
}

Ivy 在 Angular 8 中默认是关闭的,但是因为我的 tsconfig.server.json 扩展了 tsconfig.app.json,并且应用配置开启了 Ivy,我必须为服务器配置明确关闭它.

Ivy is off by default in Angular 8, but because my tsconfig.server.json extends tsconfig.app.json, and the app config had Ivy turned on, I had to explicitly turn it off for the server config.

毕竟,服务器对内容的请求实际上开始为我工作.

After all this, server requests for content actually started to work for me.

如果这对您没有帮助,我建议您下载 angular 文档中提到的通用示例项目:

If this doesn't help you, I would recommend downloading the universal example project mentioned in the angular docs:

下载:https://angular.io/generated/zips/universal/universal.邮编文档:https://angular.io/guide/universal

下载后,比较所有相关文件以确保您拥有相同的文件.如果您在自己的项目中仍然遇到错误,但示例正在运行,那么请尝试将您的设置文件、模块和组件等一一移动到示例项目中,看看是什么破坏了它.这就是我如何能够确定是我的 tsconfig.server.json 文件破坏了它.

Once downloaded, compare all the relevant files to make sure you have the same. If you still get errors in your own project, but the example is working, then, try moving your settings files, modules and components etc inside the example project one by one and see what breaks it. This is how I was able to identify that it was my tsconfig.server.json file that broke it.

这篇关于Angular-universal 获取错误:您必须传入 NgModule 或 NgModuleFactory 才能引导的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-04 11:02
查看更多