Update to Angular 4 (but leave directory name as Angular2Spa until finished)

This commit is contained in:
Steve Sanderson
2017-05-17 10:13:46 +01:00
parent b5636ea871
commit 5ade33b870
9 changed files with 132 additions and 106 deletions

View File

@@ -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.shared';
@NgModule({
bootstrap: sharedConfig.bootstrap,
declarations: sharedConfig.declarations,
imports: [
BrowserModule,
FormsModule,
HttpModule,
...sharedConfig.imports
],
providers: [
{ provide: 'ORIGIN_URL', useValue: location.origin }
]
})
export class AppModule {
}

View File

@@ -0,0 +1,14 @@
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { sharedConfig } from './app.module.shared';
@NgModule({
bootstrap: sharedConfig.bootstrap,
declarations: sharedConfig.declarations,
imports: [
ServerModule,
...sharedConfig.imports
]
})
export class AppModule {
}

View File

@@ -1,13 +1,13 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { UniversalModule } from 'angular2-universal';
import { AppComponent } from './components/app/app.component'
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
import { CounterComponent } from './components/counter/counter.component';
@NgModule({
export const sharedConfig: NgModule = {
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
@@ -17,7 +17,6 @@ import { CounterComponent } from './components/counter/counter.component';
HomeComponent
],
imports: [
UniversalModule, // Must be first import. This automatically imports BrowserModule, HttpModule, and JsonpModule too.
RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
@@ -26,6 +25,4 @@ import { CounterComponent } from './components/counter/counter.component';
{ path: '**', redirectTo: 'home' }
])
]
})
export class AppModule {
}
};

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { Component, Inject } from '@angular/core';
import { Http } from '@angular/http';
@Component({
@@ -8,8 +8,8 @@ import { Http } from '@angular/http';
export class FetchDataComponent {
public forecasts: WeatherForecast[];
constructor(http: Http) {
http.get('/api/SampleData/WeatherForecasts').subscribe(result => {
constructor(http: Http, @Inject('ORIGIN_URL') originUrl: string) {
http.get(originUrl + '/api/SampleData/WeatherForecasts').subscribe(result => {
this.forecasts = result.json() as WeatherForecast[];
});
}

View File

@@ -1,29 +1,22 @@
import 'angular2-universal-polyfills/browser';
import 'reflect-metadata';
import 'zone.js';
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);

View File

@@ -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 requestZone.run<Promise<string>>(() => platform.serializeModule(AppModule)).then(html => {
resolve({ html: html });
}, reject);
return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
const appRef = moduleRef.injector.get(ApplicationRef);
const state = moduleRef.injector.get(PlatformState);
const zone = moduleRef.injector.get(NgZone);
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();
});
});
});
});
});

View File

@@ -1,5 +1,6 @@
// Load required polyfills and testing libraries
import 'angular2-universal-polyfills';
import 'reflect-metadata';
import 'zone.js';
import 'zone.js/dist/long-stack-trace-zone';
import 'zone.js/dist/proxy.js';
import 'zone.js/dist/sync-test';