Working with imports with split app modules in Ang

2019-05-25 21:46发布

问题:

I created a .net spa application with Angular for the front end using the yeoman generator. It creates an app with three app.module.ts files (app.module.client.ts, app.module.shared.ts, app.module.server.ts). Unfortunately the NgModule imports which are located in client app module file do not seem to be properly injected into the components. So when I try to use ngFor on an input I am given the error "Can't bind to 'ngModel' since it isn't a known property of 'input'." I think this is because the forms module is not being imported even though my app.module.client.ts file looks like this: import { NgModule } from '@angular/core';

import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { sharedConfig } from './app.module.shared';
import {SelectModule} from 'ng2-select';

    @NgModule({
        bootstrap: sharedConfig.bootstrap,
        declarations: sharedConfig.declarations,
        imports: [
            BrowserModule,
            FormsModule,
            HttpModule,
            SelectModule,
            ...sharedConfig.imports
        ],
        providers: [
            { provide: 'ORIGIN_URL', useValue: location.origin }
        ]
    })
    export class AppModule {
    }

Perhaps I'm missing something on how these divided modules work. How do I properly import modules given this different module architecture?

Update: I was able to solve the problem by including the module imports in the app.module.server.ts as well. I want to leave this question open and hopefully someone can explain how this works. It's especially strange since the prebuilt template from Yeoman has all the module imports in the app.module.client.ts file and they seem impotent when running the website with dotnet run they had to be duplicated in the server file as well.

回答1:

The ASP.NET Core SPA with Angular 4 template splits the AppModule into three files so that Webpack will be able to efficiently compile the client-side bundle (suitable for running in browsers) and the server-side prerendering bundle (meant for running in Node).

More specifically, such approach expects that:

  • browser-specific modules and providers will be put in the app.module.browser.ts file.

  • server-specific modules and providers will be put in the app.module.server.ts file.

  • anything needed regardless of the executing context will be put in the app.module.shared.ts file.

This will allow two different bootstrap cycles: one for the browsers (shared + browser) and one for the server (shared + server). The main difference between the two is that the first one imports the BrowserModule from the @angular/platform-browser package, while the latter imports the ServerModule from the @angular/platform-server package - which is required to render Angular app pages from the server.

That said, you should put all your app references into the app.module.shared.ts file and write your Angular code using an isomorphic approach - meaning that it will work regardless of the executing context: all the rest should be handled by the default Webpack configuration shipped with the template, which will generate the appropriate client & server builds for JIT, AoT & server-side rendering.

For some (very) basic info about how to start writing isomorphic code in Angular and/or identify the executing environment and react accordingly, check out this blog post that I wrote on the topic.