I'm trying to create an Angular Universal app building a specific route at build time to inject an AppShell into Index.html page as a means to get meaningful content in front of the user as soon as possible (while Angular app is downloading). I am following the instructions found at Angular University and I have it working as instructed. However, as I systematically add my existing app's functionality into this test app, I'm running into 'Unexpected token import' error stemming from a 3rd party plugin I need for the browser app, but is not needed for the very simple server app I'm trying to build and inject into Index.html as my App Shell.
I've spent several hours researching this and found a lot of posts out there recommending webpack, however, I'm using Angular CLI. I'm not sure how to incorporate webpack with Angular CLI build process.
I found this post that seems to indicate you can exclude modules from Angular Universal build using exclude portion of tsconfig.server.json. I've tried listing the modules to exclude in tsconfig.server.json, however, I'm still getting 'Unexpected token import' error.
I have a single route I need to build and inject into my Index page. I would hope/think I should be able to exclude the vast majority of my app as it has nothing to do with creating an AppShell. I would think Angular should simply build the necessary bits needed for the route(s) registered for the server app.
I'm using Angular CLI version 1.7.3 and Angular version 5.2.4.
Does anyone have insight as to how to exclude unnecessary modules from being built into an Angular Universal app?
Thanks for your time.
You can try and create a third module exclusively for the browser.
This BrowserModule will include everything you need in your browser.
import { NgModule } from '@angular/core';
import { AppModule} from './app.module';
import { AppComponent } from './app.component';
// other browser exlusive imports
@NgModule({
imports: [
// browser exlusive imports
AppModule
],
bootstrap: [AppComponent]
})
export class AppBrowserModule { }
Then you bootstrap this new Module instead of your AppModule in the main.ts file. This approach is also discussed in this StackOverflow question: Need BrowserAnimationsModule in Angular but gives error in Universal
Here the module to exclude on the Server was the BrowserAnimationsModule, but it should be pretty much the same for any other module you don't need on the server.
If you need to include scripts into the browser you can import them in your main.ts and they should be available to your whole application.
If you need to check if you are on the browser you can use isPlatformBrowser from angular: https://angular.io/api/common/isPlatformBrowser
The @Optional Decorator can also be very handy(https://angular.io/api/core/Optional), if you want to include something via Dependency Injection only on the browser in one of your components or services.
You might need those to prevent errors, that are caused because files are missing on the server rendered application.
The import-bug you are describing is caused by third-party libraries not structuring their libraries correctly. An easy way to resolve this issue is to use babel on the files throwing those errors.
For example if you have a package called "failingPackage":
babel ./node_modules/failingPackage -d ./node_modules/failingPackage --presets es2015
You are basically compiling all files of the broken package and are overwriting the old files with the new ones. I'm telling you about this approach, because you might run into this error on a package that you would want to use on your server rendered application.
You can read more about this issue here: https://github.com/angular/angular-cli/issues/7200
The solution with babel is discussed here: https://github.com/SebastianM/angular-google-maps/issues/1052
Full credit goes to Anthony Nahas.
Alternatively you could try this solution: "Unexpected token import" error with Angular4 + Universal + ng-bootstrap
- Use ng eject to create a webpack.config.js
- install webpack-node-externals
- whitelist the failing packages in your externals sections of the webpack.config.js
The following approach works for me. Let's say I want to use the browser animation library PIXI.js. So, here it goes. Note that I am using the import
within the if (this.isBrowser)
condition. So, the library will be imported and executed only when the platform is a browser.
import { AfterViewInit, Component, OnInit, Inject, PLATFORM_ID } from '@angular/core';
import { Router } from '@angular/router';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
@Component({
selector: 'app-front-face',
templateUrl: './front-face.component.html',
styleUrls: ['./front-face.component.scss']
})
export class FrontFaceComponent implements OnInit, AfterViewInit {
constructor(private router: Router,
@Inject(PLATFORM_ID) private platformId: string) { }
ngOnInit() {
this.isBrowser = isPlatformBrowser(this.platformId);
}
constructor(private router: Router,
@Inject(PLATFORM_ID) private platformId: string) { }
ngOnInit() {
this.isBrowser = isPlatformBrowser(this.platformId);
}
ngAfterViewInit() {
if (this.isBrowser) {
import('pixi.js').then(PIXI => {
this.renderer = PIXI.autoDetectRenderer({ transparent: true });
this.stage = new PIXI.Container();
// Other PIXI related calls
});
}
}
}