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 { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { UniversalModule } from 'angular2-universal';
import { AppComponent } from './components/app/app.component' import { AppComponent } from './components/app/app.component'
import { NavMenuComponent } from './components/navmenu/navmenu.component'; import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component'; import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component'; import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
import { CounterComponent } from './components/counter/counter.component'; import { CounterComponent } from './components/counter/counter.component';
@NgModule({ export const sharedConfig: NgModule = {
bootstrap: [ AppComponent ], bootstrap: [ AppComponent ],
declarations: [ declarations: [
AppComponent, AppComponent,
@@ -17,7 +17,6 @@ import { CounterComponent } from './components/counter/counter.component';
HomeComponent HomeComponent
], ],
imports: [ imports: [
UniversalModule, // Must be first import. This automatically imports BrowserModule, HttpModule, and JsonpModule too.
RouterModule.forRoot([ RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' }, { path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent }, { path: 'home', component: HomeComponent },
@@ -26,6 +25,4 @@ import { CounterComponent } from './components/counter/counter.component';
{ path: '**', redirectTo: 'home' } { 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'; import { Http } from '@angular/http';
@Component({ @Component({
@@ -8,8 +8,8 @@ import { Http } from '@angular/http';
export class FetchDataComponent { export class FetchDataComponent {
public forecasts: WeatherForecast[]; public forecasts: WeatherForecast[];
constructor(http: Http) { constructor(http: Http, @Inject('ORIGIN_URL') originUrl: string) {
http.get('/api/SampleData/WeatherForecasts').subscribe(result => { http.get(originUrl + '/api/SampleData/WeatherForecasts').subscribe(result => {
this.forecasts = result.json() as WeatherForecast[]; 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 { enableProdMode } from '@angular/core';
import { platformUniversalDynamic } from 'angular2-universal'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module'; import { AppModule } from './app/app.module.client';
import 'bootstrap';
const rootElemTagName = 'app'; // Update this if you change your root component selector
// Enable either Hot Module Reloading or production mode
if (module['hot']) { if (module['hot']) {
module['hot'].accept(); module['hot'].accept();
module['hot'].dispose(() => { module['hot'].dispose(() => {
// Before restarting the app, we create a new root element and dispose the old one // Before restarting the app, we create a new root element and dispose the old one
const oldRootElem = document.querySelector(rootElemTagName); const oldRootElem = document.querySelector('app');
const newRootElem = document.createElement(rootElemTagName); const newRootElem = document.createElement('app');
oldRootElem.parentNode.insertBefore(newRootElem, oldRootElem); oldRootElem.parentNode.insertBefore(newRootElem, oldRootElem);
platform.destroy(); modulePromise.then(appModule => appModule.destroy());
}); });
} else { } else {
enableProdMode(); enableProdMode();
} }
// Boot the application, either now or when the DOM content is loaded // Note: @ng-tools/webpack looks for the following expression when performing production
const platform = platformUniversalDynamic(); // builds. Don't change how this line looks, otherwise you may break tree-shaking.
const bootApplication = () => { platform.bootstrapModule(AppModule); }; const modulePromise = platformBrowserDynamic().bootstrapModule(AppModule);
if (document.readyState === 'complete') {
bootApplication();
} else {
document.addEventListener('DOMContentLoaded', bootApplication);
}

View File

@@ -1,34 +1,36 @@
import 'angular2-universal-polyfills'; import 'reflect-metadata';
import 'angular2-universal-patch';
import 'zone.js'; 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 { createServerRenderer, RenderResult } from 'aspnet-prerendering';
import { enableProdMode } from '@angular/core'; import { AppModule } from './app/app.module.server';
import { platformNodeDynamic } from 'angular2-universal';
import { AppModule } from './app/app.module';
enableProdMode(); enableProdMode();
const platform = platformNodeDynamic();
export default createServerRenderer(params => { export default createServerRenderer(params => {
return new Promise<RenderResult>((resolve, reject) => { const providers = [
const requestZone = Zone.current.fork({ { provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
name: 'angular-universal request', { provide: 'ORIGIN_URL', useValue: params.origin }
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;
}
});
return requestZone.run<Promise<string>>(() => platform.serializeModule(AppModule)).then(html => { return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
resolve({ html: html }); const appRef = moduleRef.injector.get(ApplicationRef);
}, reject); 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 // 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/long-stack-trace-zone';
import 'zone.js/dist/proxy.js'; import 'zone.js/dist/proxy.js';
import 'zone.js/dist/sync-test'; import 'zone.js/dist/sync-test';

View File

@@ -5,58 +5,56 @@
"test": "karma start ClientApp/test/karma.conf.js" "test": "karma start ClientApp/test/karma.conf.js"
}, },
"dependencies": { "dependencies": {
"@angular/common": "^2.4.5", "@angular/animations": "4.1.2",
"@angular/compiler": "^2.4.5", "@angular/common": "4.1.2",
"@angular/core": "^2.4.5", "@angular/compiler": "4.1.2",
"@angular/forms": "^2.4.5", "@angular/core": "4.1.2",
"@angular/http": "^2.4.5", "@angular/forms": "4.1.2",
"@angular/platform-browser": "^2.4.5", "@angular/http": "4.1.2",
"@angular/platform-browser-dynamic": "^2.4.5", "@angular/platform-browser": "4.1.2",
"@angular/platform-server": "^2.4.5", "@angular/platform-browser-dynamic": "4.1.2",
"@angular/router": "^3.4.5", "@angular/platform-server": "4.1.2",
"@types/node": "^6.0.42", "@angular/router": "4.1.2",
"angular2-platform-node": "~2.0.11", "@types/node": "7.0.18",
"angular2-template-loader": "^0.6.2", "angular2-template-loader": "0.6.2",
"angular2-universal": "^2.1.0-rc.1", "aspnet-prerendering": "^2.0.5",
"angular2-universal-patch": "^0.2.1", "aspnet-webpack": "^1.0.29",
"angular2-universal-polyfills": "^2.1.0-rc.1", "awesome-typescript-loader": "3.1.3",
"aspnet-prerendering": "^2.0.0", "bootstrap": "3.3.7",
"aspnet-webpack": "^1.0.17", "css": "2.2.1",
"awesome-typescript-loader": "^3.0.0", "css-loader": "0.28.1",
"bootstrap": "^3.3.7", "es6-shim": "0.35.3",
"css": "^2.2.1", "event-source-polyfill": "0.0.9",
"css-loader": "^0.25.0", "expose-loader": "0.7.3",
"es6-shim": "^0.35.1", "extract-text-webpack-plugin": "2.1.0",
"event-source-polyfill": "^0.0.7", "file-loader": "0.11.1",
"expose-loader": "^0.7.1", "html-loader": "0.4.5",
"extract-text-webpack-plugin": "^2.0.0-rc", "isomorphic-fetch": "2.2.1",
"file-loader": "^0.9.0", "jquery": "3.2.1",
"html-loader": "^0.4.4", "json-loader": "0.5.4",
"isomorphic-fetch": "^2.2.1", "preboot": "4.5.2",
"jquery": "^2.2.1", "raw-loader": "0.5.1",
"json-loader": "^0.5.4", "reflect-metadata": "0.1.10",
"preboot": "^4.5.2", "rxjs": "5.4.0",
"raw-loader": "^0.5.1", "style-loader": "0.17.0",
"rxjs": "^5.0.1", "to-string-loader": "1.1.5",
"style-loader": "^0.13.1", "typescript": "2.3.2",
"to-string-loader": "^1.1.5", "url-loader": "0.5.8",
"typescript": "^2.2.1", "webpack": "2.5.1",
"url-loader": "^0.5.7", "webpack-hot-middleware": "2.18.0",
"webpack": "^2.2.0", "webpack-merge": "4.1.0",
"webpack-hot-middleware": "^2.12.2", "zone.js": "0.8.10"
"webpack-merge": "^0.14.1",
"zone.js": "^0.7.6"
}, },
"devDependencies": { "devDependencies": {
"@types/chai": "^3.4.34", "@types/chai": "3.5.2",
"@types/jasmine": "^2.5.37", "@types/jasmine": "2.5.47",
"chai": "^3.5.0", "chai": "3.5.0",
"jasmine-core": "^2.5.2", "jasmine-core": "2.6.1",
"karma": "^1.3.0", "karma": "1.7.0",
"karma-chai": "^0.1.0", "karma-chai": "0.1.0",
"karma-chrome-launcher": "^2.0.0", "karma-chrome-launcher": "2.1.1",
"karma-cli": "^1.0.1", "karma-cli": "1.0.1",
"karma-jasmine": "^1.0.2", "karma-jasmine": "1.1.0",
"karma-webpack": "^1.8.0" "karma-webpack": "2.0.3"
} }
} }

View File

@@ -16,16 +16,15 @@ module.exports = (env) => {
}, },
entry: { entry: {
vendor: [ vendor: [
'@angular/animations',
'@angular/common', '@angular/common',
'@angular/compiler', '@angular/compiler',
'@angular/core', '@angular/core',
'@angular/forms',
'@angular/http', '@angular/http',
'@angular/platform-browser', '@angular/platform-browser',
'@angular/platform-browser-dynamic', '@angular/platform-browser-dynamic',
'@angular/router', '@angular/router',
'@angular/platform-server',
'angular2-universal',
'angular2-universal-polyfills',
'bootstrap', 'bootstrap',
'bootstrap/dist/css/bootstrap.css', 'bootstrap/dist/css/bootstrap.css',
'es6-shim', 'es6-shim',
@@ -43,6 +42,7 @@ module.exports = (env) => {
plugins: [ plugins: [
new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable) new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/11580 new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/11580
new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)@angular/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/14898
new webpack.IgnorePlugin(/^vertx$/) // Workaround for https://github.com/stefanpenner/es6-promise/issues/100 new webpack.IgnorePlugin(/^vertx$/) // Workaround for https://github.com/stefanpenner/es6-promise/issues/100
] ]
}; };