Update to Angular 4 (phase 1) #930
Changes from all commits
5aeba2b
99f981b
b703e5a
1713224
57bdd90
bc2c2e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
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'; | ||
|
||
@NgModule({ | ||
bootstrap: sharedConfig.bootstrap, | ||
declarations: sharedConfig.declarations, | ||
imports: [ | ||
BrowserModule, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You want to make sure you have
|
||
FormsModule, | ||
HttpModule, | ||
...sharedConfig.imports | ||
], | ||
providers: [ | ||
{ provide: 'ORIGIN_URL', useValue: location.origin } | ||
] | ||
}) | ||
export class AppModule { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { NgModule } from '@angular/core'; | ||
import { ServerModule } from '@angular/platform-server'; | ||
import { sharedConfig } from './app.module'; | ||
|
||
@NgModule({ | ||
bootstrap: sharedConfig.bootstrap, | ||
declarations: sharedConfig.declarations, | ||
imports: [ | ||
ServerModule, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You want to make sure you have BrowserModule with serverTransition here as well (with the same appId)
|
||
...sharedConfig.imports | ||
] | ||
}) | ||
export class AppModule { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,22 @@ | ||
import 'angular2-universal-polyfills/browser'; | ||
import 'reflect-metadata'; | ||
import 'zone.js'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could just have (I believe it's a bit smaller) |
||
import { enableProdMode } from '@angular/core'; | ||
import { platformUniversalDynamic } from 'angular2-universal'; | ||
import { AppModule } from './app/app.module'; | ||
import 'bootstrap'; | ||
const rootElemTagName = 'app'; // Update this if you change your root component selector | ||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; | ||
import { AppModule } from './app/app.module.client'; | ||
|
||
// Enable either Hot Module Reloading or production mode | ||
if (module['hot']) { | ||
module['hot'].accept(); | ||
module['hot'].dispose(() => { | ||
// Before restarting the app, we create a new root element and dispose the old one | ||
const oldRootElem = document.querySelector(rootElemTagName); | ||
const newRootElem = document.createElement(rootElemTagName); | ||
const oldRootElem = document.querySelector('app'); | ||
const newRootElem = document.createElement('app'); | ||
oldRootElem.parentNode.insertBefore(newRootElem, oldRootElem); | ||
platform.destroy(); | ||
modulePromise.then(appModule => appModule.destroy()); | ||
}); | ||
} else { | ||
enableProdMode(); | ||
} | ||
|
||
// Boot the application, either now or when the DOM content is loaded | ||
const platform = platformUniversalDynamic(); | ||
const bootApplication = () => { platform.bootstrapModule(AppModule); }; | ||
if (document.readyState === 'complete') { | ||
bootApplication(); | ||
} else { | ||
document.addEventListener('DOMContentLoaded', bootApplication); | ||
} | ||
// Note: @ng-tools/webpack looks for the following expression when performing production | ||
// builds. Don't change how this line looks, otherwise you may break tree-shaking. | ||
const modulePromise = platformBrowserDynamic().bootstrapModule(AppModule); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,36 @@ | ||
import 'angular2-universal-polyfills'; | ||
import 'angular2-universal-patch'; | ||
import 'reflect-metadata'; | ||
import 'zone.js'; | ||
import 'rxjs/add/operator/first'; | ||
import { enableProdMode, ApplicationRef, NgZone, ValueProvider } from '@angular/core'; | ||
import { platformDynamicServer, PlatformState, INITIAL_CONFIG } from '@angular/platform-server'; | ||
import { createServerRenderer, RenderResult } from 'aspnet-prerendering'; | ||
import { enableProdMode } from '@angular/core'; | ||
import { platformNodeDynamic } from 'angular2-universal'; | ||
import { AppModule } from './app/app.module'; | ||
import { AppModule } from './app/app.module.server'; | ||
|
||
enableProdMode(); | ||
const platform = platformNodeDynamic(); | ||
|
||
export default createServerRenderer(params => { | ||
return new Promise<RenderResult>((resolve, reject) => { | ||
const requestZone = Zone.current.fork({ | ||
name: 'angular-universal request', | ||
properties: { | ||
baseUrl: '/', | ||
requestUrl: params.url, | ||
originUrl: params.origin, | ||
preboot: false, | ||
document: '<app></app>' | ||
}, | ||
onHandleError: (parentZone, currentZone, targetZone, error) => { | ||
// If any error occurs while rendering the module, reject the whole operation | ||
reject(error); | ||
return true; | ||
} | ||
}); | ||
const providers = [ | ||
{ provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } }, | ||
{ provide: 'ORIGIN_URL', useValue: params.origin } | ||
]; | ||
|
||
return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The team has stressed to never use dynamicServer as JIT compilation just slows down server renders tremendously as you'd really only want ahead of time compiled ones with Just worried we might have Devs use this phase 1 template (and never update it) and get stuck using a non-ideal bootstrapModule serializer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good point as for where we're ultimately heading, but right now people are already using the equivalent to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great! Just making sure :) |
||
const appRef = moduleRef.injector.get(ApplicationRef); | ||
const state = moduleRef.injector.get(PlatformState); | ||
const zone = moduleRef.injector.get(NgZone); | ||
|
||
return requestZone.run<Promise<string>>(() => platform.serializeModule(AppModule)).then(html => { | ||
resolve({ html: html }); | ||
}, reject); | ||
return new Promise<RenderResult>((resolve, reject) => { | ||
zone.onError.subscribe(errorInfo => reject(errorInfo)); | ||
appRef.isStable.first(isStable => isStable).subscribe(() => { | ||
// Because 'onStable' fires before 'onError', we have to delay slightly before | ||
// completing the request in case there's an error to report | ||
setImmediate(() => { | ||
resolve({ | ||
html: state.renderToString() | ||
}); | ||
moduleRef.destroy(); | ||
}); | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could catch errors here in the subscription |
||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might make more sense to have sharedConfig be an
NgModule
itself that they import withinimports[]
, since most Apps will have lots of NgModules lazy-loaded etc ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. How does that then work in terms of overriding the config in each of the server/client app modules? If you can post a code sample we can definitely consider this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW I tried to do this, but hit a roadblock. If the shared
NgModule
includes the routing config (which of course you would want it to), then it has to be able to compile all the templates for all the components. But to be able to compile them, it needs to reference eitherBrowserModule
orServerModule
(otherwise it fails with errors likengIf is unknown
). But it can't reference either of those, because it needs to work with both server and client.So is there any reasonable way to do this? Please let me know if there is. It doesn't seem like a compiled
NgModule
can possibly be portable across both server and client, which is an unfortunate limitation.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry just seeing this now! @SteveSandersonMS
Did you have boot-client & boot-server using browser-app.module & server-app.module ?
To get rid of the ngIf unknown errors, you just import CommonModule into your base app.module NgModule (https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/Client/app/app.module.ts#L51)
You can see in there I have the app.module importing the Routes, and all the base declarations etc that can be shared. Browser & Server-app.module's both import the app.module and they each declare the
bootstrap: [ AppComponent ]
themselves. You want to make sure app.module doesn't declare a bootstrap Component.