From 40bf6c19d233b713fde87330ee50660ed35fd668 Mon Sep 17 00:00:00 2001 From: Mark Pieszak Date: Tue, 23 Jan 2018 15:02:42 -0500 Subject: [PATCH 01/60] Feature(Angular 5.0): Upgrade to working copy of 5.0 (#538) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This works both in Development & Production. 🎁 (Apologies on the delay) Now dotnet publish won't fail, and you can successfully build Production builds. In the future, boot.server.PRODUCTION.ts probably won't be needed. We'll be working on upgrading this to use the CLI version that Steve Sanderson has been working on the past few months - this will clean-up a lot of the configuration so that it's all handled by the CLI itself. (Aka: no more webpack files for us to deal with here) ✨ This should close out many issues: #535 #482 (others as well, can't find them at the moment) --- ClientApp/app/app.component.html | 2 +- ClientApp/app/app.component.ts | 11 +- ClientApp/app/app.module.browser.ts | 10 +- ClientApp/app/app.module.server.ts | 25 +- ClientApp/app/app.module.ts | 28 +- .../components/navmenu/navmenu.component.html | 2 +- .../components/navmenu/navmenu.component.ts | 10 +- .../user-detail/user-detail.component.ts | 2 +- .../containers/counter/counter.component.ts | 2 +- .../app/containers/home/home.component.html | 12 +- .../app/containers/home/home.component.ts | 2 +- .../app/containers/lazy/lazy.component.ts | 2 +- .../not-found/not-found.component.ts | 2 +- .../app/containers/users/users.component.html | 2 +- .../app/containers/users/users.component.ts | 21 +- .../app/shared/constants/baseurl.constants.ts | 3 - ClientApp/app/shared/constants/request.ts | 3 - ClientApp/app/shared/user.service.ts | 53 ++-- ClientApp/boot.browser.ts | 2 - ClientApp/boot.server.PRODUCTION.ts | 38 +++ ClientApp/boot.server.ts | 18 +- .../transfer-http/transfer-http.module.ts | 10 - .../modules/transfer-http/transfer-http.ts | 152 ----------- .../browser-transfer-state.module.ts | 20 -- .../server-transfer-state.module.ts | 12 - .../transfer-state/server-transfer-state.ts | 36 --- .../modules/transfer-state/transfer-state.ts | 40 --- .../polyfills/temporary-aspnetcore-engine.ts | 254 ------------------ ClientApp/tsconfig.app.json | 3 +- README.md | 39 +-- Server/Controllers/HomeController.cs | 2 +- Startup.cs | 4 +- Views/Home/Index.cshtml | 6 +- package.json | 40 +-- tslint.json | 3 +- webpack.config.js | 63 +++-- webpack.config.vendor.js | 2 +- 37 files changed, 232 insertions(+), 704 deletions(-) delete mode 100644 ClientApp/app/shared/constants/baseurl.constants.ts delete mode 100644 ClientApp/app/shared/constants/request.ts create mode 100644 ClientApp/boot.server.PRODUCTION.ts delete mode 100644 ClientApp/modules/transfer-http/transfer-http.module.ts delete mode 100644 ClientApp/modules/transfer-http/transfer-http.ts delete mode 100644 ClientApp/modules/transfer-state/browser-transfer-state.module.ts delete mode 100644 ClientApp/modules/transfer-state/server-transfer-state.module.ts delete mode 100644 ClientApp/modules/transfer-state/server-transfer-state.ts delete mode 100644 ClientApp/modules/transfer-state/transfer-state.ts delete mode 100644 ClientApp/polyfills/temporary-aspnetcore-engine.ts diff --git a/ClientApp/app/app.component.html b/ClientApp/app/app.component.html index a3e3bf9b..0345c682 100644 --- a/ClientApp/app/app.component.html +++ b/ClientApp/app/app.component.html @@ -1,5 +1,5 @@
- +
diff --git a/ClientApp/app/app.component.ts b/ClientApp/app/app.component.ts index 02b91787..7a6a75ed 100644 --- a/ClientApp/app/app.component.ts +++ b/ClientApp/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, OnDestroy, Inject, ViewEncapsulation, RendererFactory2, PLATFORM_ID } from '@angular/core'; +import { Component, OnInit, OnDestroy, Inject, ViewEncapsulation, RendererFactory2, PLATFORM_ID, Injector } from '@angular/core'; import { Router, NavigationEnd, ActivatedRoute, PRIMARY_OUTLET } from '@angular/router'; import { Meta, Title, DOCUMENT, MetaDefinition } from '@angular/platform-browser'; import { Subscription } from 'rxjs/Subscription'; @@ -7,10 +7,10 @@ import { LinkService } from './shared/link.service'; // i18n support import { TranslateService } from '@ngx-translate/core'; -import { REQUEST } from './shared/constants/request'; +import { REQUEST } from '@nguniversal/aspnetcore-engine'; @Component({ - selector: 'app', + selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], encapsulation: ViewEncapsulation.None @@ -23,6 +23,7 @@ export class AppComponent implements OnInit, OnDestroy { private defaultPageTitle: string = 'My App'; private routerSub$: Subscription; + private request; constructor( private router: Router, @@ -31,7 +32,7 @@ export class AppComponent implements OnInit, OnDestroy { private meta: Meta, private linkService: LinkService, public translate: TranslateService, - @Inject(REQUEST) private request + private injector: Injector ) { // this language will be used as a fallback when a translation isn't found in the current language translate.setDefaultLang('en'); @@ -39,6 +40,8 @@ export class AppComponent implements OnInit, OnDestroy { // the lang to use, if the lang isn't available, it will use the current loader to get them translate.use('en'); + this.request = this.injector.get(REQUEST); + console.log(`What's our REQUEST Object look like?`); console.log(`The Request object only really exists on the Server, but on the Browser we can at least see Cookies`); console.log(this.request); diff --git a/ClientApp/app/app.module.browser.ts b/ClientApp/app/app.module.browser.ts index ac318d44..56d1c20c 100644 --- a/ClientApp/app/app.module.browser.ts +++ b/ClientApp/app/app.module.browser.ts @@ -3,12 +3,10 @@ import { BrowserModule } from '@angular/platform-browser'; import { APP_BASE_HREF } from '@angular/common'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { ORIGIN_URL } from './shared/constants/baseurl.constants'; +import { ORIGIN_URL, REQUEST } from '@nguniversal/aspnetcore-engine'; import { AppModuleShared } from './app.module'; import { AppComponent } from './app.component'; -import { REQUEST } from './shared/constants/request'; -import { BrowserTransferStateModule } from '../modules/transfer-state/browser-transfer-state.module'; - +import { BrowserTransferStateModule } from '@angular/platform-browser'; import { BrowserPrebootModule } from 'preboot/browser'; export function getOriginUrl() { @@ -23,12 +21,8 @@ export function getRequest() { @NgModule({ bootstrap: [AppComponent], imports: [ - BrowserModule.withServerTransition({ - appId: 'my-app-id' // make sure this matches with your Server NgModule - }), BrowserPrebootModule.replayEvents(), BrowserAnimationsModule, - BrowserTransferStateModule, // Our Common AppModule AppModuleShared diff --git a/ClientApp/app/app.module.server.ts b/ClientApp/app/app.module.server.ts index 9e22b71e..25711851 100644 --- a/ClientApp/app/app.module.server.ts +++ b/ClientApp/app/app.module.server.ts @@ -5,33 +5,28 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { AppModuleShared } from './app.module'; import { AppComponent } from './app.component'; -import { ServerTransferStateModule } from '../modules/transfer-state/server-transfer-state.module'; -import { TransferState } from '../modules/transfer-state/transfer-state'; +import { ServerTransferStateModule } from '@angular/platform-server'; import { ServerPrebootModule } from 'preboot/server'; @NgModule({ bootstrap: [AppComponent], imports: [ - BrowserModule.withServerTransition({ - appId: 'my-app-id' // make sure this matches with your Browser NgModule - }), + // Our Common AppModule + AppModuleShared, + ServerModule, - ServerPrebootModule.recordEvents({ appRoot: 'app' }), + ServerPrebootModule.recordEvents({ appRoot: 'app-root' }), NoopAnimationsModule, - ServerTransferStateModule, - - // Our Common AppModule - AppModuleShared + // HttpTransferCacheModule still needs fixes for 5.0 + // Leave this commented out for now, as it breaks Server-renders + // Looking into fixes for this! - @MarkPieszak + // ServerTransferStateModule // <-- broken for the time-being with ASP.NET ] }) export class AppModule { - constructor(private transferState: TransferState) { } + constructor() { } - // Gotcha (needs to be an arrow function) - ngOnBootstrap = () => { - this.transferState.inject(); - } } diff --git a/ClientApp/app/app.module.ts b/ClientApp/app/app.module.ts index 1c492f1d..d31250c4 100644 --- a/ClientApp/app/app.module.ts +++ b/ClientApp/app/app.module.ts @@ -2,7 +2,10 @@ import { NgModule, Inject } from '@angular/core'; import { RouterModule, PreloadAllModules } from '@angular/router'; import { CommonModule, APP_BASE_HREF } from '@angular/common'; import { HttpModule, Http } from '@angular/http'; +import { HttpClientModule, HttpClient } from '@angular/common/http'; import { FormsModule } from '@angular/forms'; +import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser'; +import { TransferHttpCacheModule } from '@nguniversal/common'; import { Ng2BootstrapModule } from 'ngx-bootstrap'; @@ -16,17 +19,14 @@ import { HomeComponent } from './containers/home/home.component'; import { UsersComponent } from './containers/users/users.component'; import { UserDetailComponent } from './components/user-detail/user-detail.component'; import { CounterComponent } from './containers/counter/counter.component'; -// import { ChatComponent } from './containers/chat/chat.component'; import { NotFoundComponent } from './containers/not-found/not-found.component'; import { NgxBootstrapComponent } from './containers/ngx-bootstrap-demo/ngx-bootstrap.component'; import { LinkService } from './shared/link.service'; import { UserService } from './shared/user.service'; -// import { ConnectionResolver } from './shared/route.resolver'; -import { ORIGIN_URL } from './shared/constants/baseurl.constants'; -import { TransferHttpModule } from '../modules/transfer-http/transfer-http.module'; +import { ORIGIN_URL } from '@nguniversal/aspnetcore-engine'; -export function createTranslateLoader(http: Http, baseHref) { +export function createTranslateLoader(http: HttpClient, baseHref) { // Temporary Azure hack if (baseHref === null && typeof window !== 'undefined') { baseHref = window.location.origin; @@ -43,24 +43,28 @@ export function createTranslateLoader(http: Http, baseHref) { UsersComponent, UserDetailComponent, HomeComponent, - // ChatComponent, NotFoundComponent, NgxBootstrapComponent ], imports: [ CommonModule, - HttpModule, + BrowserModule.withServerTransition({ + appId: 'my-app-id' // make sure this matches with your Server NgModule + }), + HttpClientModule, + TransferHttpCacheModule, + BrowserTransferStateModule, + + FormsModule, Ng2BootstrapModule.forRoot(), // You could also split this up if you don't want the Entire Module imported - TransferHttpModule, // Our Http TransferData method - // i18n support TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: (createTranslateLoader), - deps: [Http, [ORIGIN_URL]] + deps: [HttpClient, [ORIGIN_URL]] } }), @@ -145,9 +149,9 @@ export function createTranslateLoader(http: Http, baseHref) { providers: [ LinkService, UserService, - // ConnectionResolver, TranslateModule - ] + ], + bootstrap: [AppComponent] }) export class AppModuleShared { } diff --git a/ClientApp/app/components/navmenu/navmenu.component.html b/ClientApp/app/components/navmenu/navmenu.component.html index bd6eadda..08e1b0fc 100644 --- a/ClientApp/app/components/navmenu/navmenu.component.html +++ b/ClientApp/app/components/navmenu/navmenu.component.html @@ -8,7 +8,7 @@ - Angular 4 Universal & ASP.NET Core + Angular 5 Universal & ASP.NET Core
diff --git a/ClientApp/app/components/navmenu/navmenu.component.ts b/ClientApp/app/components/navmenu/navmenu.component.ts index ac36f036..fb0cf22c 100644 --- a/ClientApp/app/components/navmenu/navmenu.component.ts +++ b/ClientApp/app/components/navmenu/navmenu.component.ts @@ -1,23 +1,23 @@ import { Component } from '@angular/core'; @Component({ - selector: 'nav-menu', + selector: 'app-nav-menu', templateUrl: './navmenu.component.html', styleUrls: ['./navmenu.component.css'] }) export class NavMenuComponent { - collapse: string = "collapse"; + collapse: string = 'collapse'; collapseNavbar(): void { if (this.collapse.length > 1) { - this.collapse = ""; + this.collapse = ''; } else { - this.collapse = "collapse"; + this.collapse = 'collapse'; } } collapseMenu() { - this.collapse = "collapse" + this.collapse = 'collapse'; } } diff --git a/ClientApp/app/components/user-detail/user-detail.component.ts b/ClientApp/app/components/user-detail/user-detail.component.ts index 9db88355..d21d3415 100644 --- a/ClientApp/app/components/user-detail/user-detail.component.ts +++ b/ClientApp/app/components/user-detail/user-detail.component.ts @@ -3,7 +3,7 @@ import { IUser } from '../../models/User'; import { UserService } from '../../shared/user.service'; @Component({ - selector: 'user-detail', + selector: 'app-user-detail', templateUrl: './user-detail.component.html' }) export class UserDetailComponent { diff --git a/ClientApp/app/containers/counter/counter.component.ts b/ClientApp/app/containers/counter/counter.component.ts index 69de17d9..5adb5195 100644 --- a/ClientApp/app/containers/counter/counter.component.ts +++ b/ClientApp/app/containers/counter/counter.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; @Component({ - selector: 'counter', + selector: 'app-counter', templateUrl: './counter.component.html' }) export class CounterComponent { diff --git a/ClientApp/app/containers/home/home.component.html b/ClientApp/app/containers/home/home.component.html index 06d801e7..3a4db63b 100644 --- a/ClientApp/app/containers/home/home.component.html +++ b/ClientApp/app/containers/home/home.component.html @@ -1,7 +1,7 @@ 

{{ title }}

- Enjoy the latest features from .NET Core & Angular 4.0! + Enjoy the latest features from .NET Core & Angular 5.0!
For more info check the repo here: AspNetCore-Angular2-Universal repo

@@ -12,7 +12,7 @@

{{ 'HOME_FEATURE_LIST_TITLE' | translate }}

- + diff --git a/ClientApp/app/containers/users/users.component.ts b/ClientApp/app/containers/users/users.component.ts index 9dd28781..35cc8517 100644 --- a/ClientApp/app/containers/users/users.component.ts +++ b/ClientApp/app/containers/users/users.component.ts @@ -7,7 +7,7 @@ import { IUser } from '../../models/User'; import { UserService } from '../../shared/user.service'; @Component({ - selector: 'users', + selector: 'app-users', templateUrl: './users.component.html', styleUrls: ['./users.component.css'], animations: [ @@ -31,15 +31,16 @@ export class UsersComponent implements OnInit { selectedUser: IUser; // Use "constructor"s only for dependency injection - constructor(private userService: UserService) { } + constructor( + private userService: UserService + ) { } // Here you want to handle anything with @Input()'s @Output()'s // Data retrieval / etc - this is when the Component is "ready" and wired up ngOnInit() { this.userService.getUsers().subscribe(result => { - console.log('Get user result: ', result); - console.log('TransferHttp [GET] /api/users/allresult', result); - this.users = result as IUser[]; + console.log('HttpClient [GET] /api/users/allresult', result); + this.users = result; }); } @@ -50,10 +51,8 @@ export class UsersComponent implements OnInit { deleteUser(user) { this.userService.deleteUser(user).subscribe(result => { console.log('Delete user result: ', result); - if (result.ok) { - let position = this.users.indexOf(user); - this.users.splice(position, 1); - } + let position = this.users.indexOf(user); + this.users.splice(position, 1); }, error => { console.log(`There was an issue. ${error._body}.`); }); @@ -62,9 +61,7 @@ export class UsersComponent implements OnInit { addUser(newUserName) { this.userService.addUser(newUserName).subscribe(result => { console.log('Post user result: ', result); - if (result.ok) { - this.users.push(result.json()); - } + this.users.push(result); }, error => { console.log(`There was an issue. ${error._body}.`); }); diff --git a/ClientApp/app/shared/constants/baseurl.constants.ts b/ClientApp/app/shared/constants/baseurl.constants.ts deleted file mode 100644 index 58807bc4..00000000 --- a/ClientApp/app/shared/constants/baseurl.constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { InjectionToken } from '@angular/core'; - -export const ORIGIN_URL = new InjectionToken('ORIGIN_URL'); diff --git a/ClientApp/app/shared/constants/request.ts b/ClientApp/app/shared/constants/request.ts deleted file mode 100644 index 4c553d8a..00000000 --- a/ClientApp/app/shared/constants/request.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { InjectionToken } from '@angular/core'; - -export const REQUEST = new InjectionToken('REQUEST'); diff --git a/ClientApp/app/shared/user.service.ts b/ClientApp/app/shared/user.service.ts index 8bcc3bb3..9489e32f 100644 --- a/ClientApp/app/shared/user.service.ts +++ b/ClientApp/app/shared/user.service.ts @@ -1,42 +1,41 @@ -import { Injectable, Inject } from '@angular/core'; +import { Injectable, Inject, Injector } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; import { Http, URLSearchParams } from '@angular/http'; import { APP_BASE_HREF } from '@angular/common'; -import { ORIGIN_URL } from './constants/baseurl.constants'; +import { ORIGIN_URL } from '@nguniversal/aspnetcore-engine'; import { IUser } from '../models/User'; -import { TransferHttp } from '../../modules/transfer-http/transfer-http'; import { Observable } from 'rxjs/Observable'; + @Injectable() export class UserService { - constructor( - private transferHttp: TransferHttp, // Use only for GETS that you want re-used between Server render -> Client render - private http: Http, // Use for everything else - @Inject(ORIGIN_URL) private baseUrl: string) { - } + private baseUrl: string; - getUsers(): Observable { - // ** TransferHttp example / concept ** - // - Here we make an Http call on the server, save the result on the window object and pass it down with the SSR, - // The Client then re-uses this Http result instead of hitting the server again! + constructor( + private http: HttpClient, + private injector: Injector + ) { + this.baseUrl = this.injector.get(ORIGIN_URL); + } - // NOTE : transferHttp also automatically does .map(res => res.json()) for you, so no need for these calls - return this.transferHttp.get(`${this.baseUrl}/api/users`); - } + getUsers() { + return this.http.get(`${this.baseUrl}/api/users`); + } - getUser(user: IUser): Observable { - return this.transferHttp.get(`${this.baseUrl}/api/users/` + user.id); - } + getUser(user: IUser) { + return this.http.get(`${this.baseUrl}/api/users/` + user.id); + } - deleteUser(user: IUser): Observable { - return this.http.delete(`${this.baseUrl}/api/users/` + user.id); - } + deleteUser(user: IUser) { + return this.http.delete(`${this.baseUrl}/api/users/` + user.id); + } - updateUser(user: IUser): Observable { - return this.http.put(`${this.baseUrl}/api/users/` + user.id, user); - } + updateUser(user: IUser){ + return this.http.put(`${this.baseUrl}/api/users/` + user.id, user); + } - addUser(newUserName: string): Observable { - return this.http.post(`${this.baseUrl}/api/users`, { name: newUserName }) - } + addUser(newUserName: string) { + return this.http.post(`${this.baseUrl}/api/users`, { name: newUserName }); + } } diff --git a/ClientApp/boot.browser.ts b/ClientApp/boot.browser.ts index a7830543..329344f7 100644 --- a/ClientApp/boot.browser.ts +++ b/ClientApp/boot.browser.ts @@ -3,8 +3,6 @@ import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module.browser'; -const rootElemTagName = 'app'; // Update this if you change your root component selector - // // Enable either Hot Module Reloading or production mode if (module['hot']) { module['hot'].accept(); diff --git a/ClientApp/boot.server.PRODUCTION.ts b/ClientApp/boot.server.PRODUCTION.ts new file mode 100644 index 00000000..30f8deb0 --- /dev/null +++ b/ClientApp/boot.server.PRODUCTION.ts @@ -0,0 +1,38 @@ +import 'zone.js/dist/zone-node'; +import './polyfills/server.polyfills'; +import { enableProdMode } from '@angular/core'; +import { createServerRenderer } from 'aspnet-prerendering'; + +// Grab the (Node) server-specific NgModule +const { AppModuleNgFactory } = require('./app/app.module.server.ngfactory'); // <-- ignore this - this is Production only +import { ngAspnetCoreEngine, IEngineOptions, createTransferScript } from '@nguniversal/aspnetcore-engine'; + +enableProdMode(); + +export default createServerRenderer((params) => { + + // Platform-server provider configuration + const setupOptions: IEngineOptions = { + appSelector: '', + ngModule: AppModuleNgFactory, + request: params, + providers: [ + // Optional - Any other Server providers you want to pass + // (remember you'll have to provide them for the Browser as well) + ] + }; + + return ngAspnetCoreEngine(setupOptions).then(response => { + + // Apply your transferData to response.globals + response.globals.transferData = createTransferScript({ + someData: 'Transfer this to the client on the window.TRANSFER_CACHE {} object', + fromDotnet: params.data.thisCameFromDotNET // example of data coming from dotnet, in HomeController + }); + + return ({ + html: response.html, // our serialized + globals: response.globals // all of our styles/scripts/meta-tags/link-tags for aspnet to serve up + }); + }); +}); diff --git a/ClientApp/boot.server.ts b/ClientApp/boot.server.ts index 86d65086..c2c0fb47 100644 --- a/ClientApp/boot.server.ts +++ b/ClientApp/boot.server.ts @@ -1,15 +1,11 @@ import 'zone.js/dist/zone-node'; import './polyfills/server.polyfills'; import { enableProdMode } from '@angular/core'; -import { INITIAL_CONFIG } from '@angular/platform-server'; -import { APP_BASE_HREF } from '@angular/common'; -import { createServerRenderer, RenderResult } from 'aspnet-prerendering'; +import { createServerRenderer } from 'aspnet-prerendering'; -import { ORIGIN_URL } from './app/shared/constants/baseurl.constants'; // Grab the (Node) server-specific NgModule import { AppModule } from './app/app.module.server'; -// Temporary * the engine will be on npm soon (`@universal/ng-aspnetcore-engine`) -import { ngAspnetCoreEngine, IEngineOptions, createTransferScript } from './polyfills/temporary-aspnetcore-engine'; +import { ngAspnetCoreEngine, IEngineOptions, createTransferScript } from '@nguniversal/aspnetcore-engine'; enableProdMode(); @@ -17,15 +13,17 @@ export default createServerRenderer((params) => { // Platform-server provider configuration const setupOptions: IEngineOptions = { - appSelector: '', + appSelector: '', ngModule: AppModule, request: params, providers: [ - // Optional - Any other Server providers you want to pass (remember you'll have to provide them for the Browser as well) + // Optional - Any other Server providers you want to pass + // (remember you'll have to provide them for the Browser as well) ] }; return ngAspnetCoreEngine(setupOptions).then(response => { + // Apply your transferData to response.globals response.globals.transferData = createTransferScript({ someData: 'Transfer this to the client on the window.TRANSFER_CACHE {} object', @@ -33,8 +31,8 @@ export default createServerRenderer((params) => { }); return ({ - html: response.html, - globals: response.globals + html: response.html, // our serialized + globals: response.globals // all of our styles/scripts/meta-tags/link-tags for aspnet to serve up }); }); }); diff --git a/ClientApp/modules/transfer-http/transfer-http.module.ts b/ClientApp/modules/transfer-http/transfer-http.module.ts deleted file mode 100644 index c2875b33..00000000 --- a/ClientApp/modules/transfer-http/transfer-http.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { NgModule } from '@angular/core'; -import { Http, HttpModule } from '@angular/http'; -import { TransferHttp } from './transfer-http'; - -@NgModule({ - providers: [ - TransferHttp - ] -}) -export class TransferHttpModule {} diff --git a/ClientApp/modules/transfer-http/transfer-http.ts b/ClientApp/modules/transfer-http/transfer-http.ts deleted file mode 100644 index 3f9b5d84..00000000 --- a/ClientApp/modules/transfer-http/transfer-http.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { Injectable, Inject, PLATFORM_ID } from '@angular/core'; -import { ConnectionBackend, Http, Request, RequestOptions, RequestOptionsArgs, Response } from '@angular/http'; -import { Observable } from 'rxjs/Observable'; -import { Subject } from 'rxjs/Subject'; -import { TransferState } from '../transfer-state/transfer-state'; -import { isPlatformServer } from '@angular/common'; - -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/do'; -import 'rxjs/add/observable/fromPromise'; - -@Injectable() -export class TransferHttp { - - private isServer = isPlatformServer(this.platformId); - - constructor( - @Inject(PLATFORM_ID) private platformId, - private http: Http, - protected transferState: TransferState - ) { } - - request(uri: string | Request, options?: RequestOptionsArgs): Observable { - return this.getData(uri, options, (url: string, options: RequestOptionsArgs) => { - return this.http.request(url, options); - }); - } - /** - * Performs a request with `get` http method. - */ - get(url: string, options?: RequestOptionsArgs): Observable { - return this.getData(url, options, (url: string, options: RequestOptionsArgs) => { - return this.http.get(url, options); - }); - } - /** - * Performs a request with `post` http method. - */ - post(url: string, body: any, options?: RequestOptionsArgs): Observable { - return this.getPostData(url, body, options, (url: string, body: any, options: RequestOptionsArgs) => { - return this.http.post(url, body, options); - }); - } - /** - * Performs a request with `put` http method. - */ - put(url: string, body: any, options?: RequestOptionsArgs): Observable { - - return this.getPostData(url, body, options, (url: string, body: any, options: RequestOptionsArgs) => { - return this.http.put(url, body, options); - }); - } - /** - * Performs a request with `delete` http method. - */ - delete(url: string, options?: RequestOptionsArgs): Observable { - return this.getData(url, options, (url: string, options: RequestOptionsArgs) => { - return this.http.delete(url, options); - }); - } - /** - * Performs a request with `patch` http method. - */ - patch(url: string, body: any, options?: RequestOptionsArgs): Observable { - return this.getPostData(url, body, options, (url: string, body: any, options: RequestOptionsArgs) => { - return this.http.patch(url, body.options); - }); - } - /** - * Performs a request with `head` http method. - */ - head(url: string, options?: RequestOptionsArgs): Observable { - return this.getData(url, options, (url: string, options: RequestOptionsArgs) => { - return this.http.head(url, options); - }); - } - /** - * Performs a request with `options` http method. - */ - options(url: string, options?: RequestOptionsArgs): Observable { - return this.getData(url, options, (url: string, options: RequestOptionsArgs) => { - return this.http.options(url, options); - }); - } - - private getData(uri: string | Request, options: RequestOptionsArgs, callback: (uri: string | Request, options?: RequestOptionsArgs) => Observable) { - - let url = uri; - - if (typeof uri !== 'string') { - url = uri.url; - } - - const key = url + JSON.stringify(options); - - try { - return this.resolveData(key); - - } catch (e) { - return callback(url, options) - .map(res => res.json()) - .do(data => { - if (this.isServer) { - this.setCache(key, data); - } - }); - } - } - - private getPostData(uri: string | Request, body: any, options: RequestOptionsArgs, callback: (uri: string | Request, body: any, options?: RequestOptionsArgs) => Observable) { - - let url = uri; - - if (typeof uri !== 'string') { - url = uri.url; - } - - const key = url + JSON.stringify(body); - - try { - - return this.resolveData(key); - - } catch (e) { - return callback(uri, body, options) - .map(res => res.json()) - .do(data => { - if (this.isServer) { - this.setCache(key, data); - } - }); - } - } - - private resolveData(key: string) { - const data = this.getFromCache(key); - - if (!data) { - throw new Error(); - } - - return Observable.fromPromise(Promise.resolve(data)); - } - - private setCache(key, data) { - return this.transferState.set(key, data); - } - - private getFromCache(key): any { - return this.transferState.get(key); - } -} diff --git a/ClientApp/modules/transfer-state/browser-transfer-state.module.ts b/ClientApp/modules/transfer-state/browser-transfer-state.module.ts deleted file mode 100644 index 20e11421..00000000 --- a/ClientApp/modules/transfer-state/browser-transfer-state.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NgModule, PLATFORM_ID } from '@angular/core'; -import { TransferState } from './transfer-state'; - -export function getTransferState(): TransferState { - const transferState = new TransferState(); - transferState.initialize(window['TRANSFER_STATE'] || {}); - return transferState; -} - -@NgModule({ - providers: [ - { - provide: TransferState, - useFactory: getTransferState - } - ] -}) -export class BrowserTransferStateModule { - -} diff --git a/ClientApp/modules/transfer-state/server-transfer-state.module.ts b/ClientApp/modules/transfer-state/server-transfer-state.module.ts deleted file mode 100644 index 1a77f653..00000000 --- a/ClientApp/modules/transfer-state/server-transfer-state.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NgModule } from '@angular/core'; -import { ServerTransferState } from './server-transfer-state'; -import { TransferState } from './transfer-state'; - -@NgModule({ - providers: [ - { provide: TransferState, useClass: ServerTransferState } - ] -}) -export class ServerTransferStateModule { - -} diff --git a/ClientApp/modules/transfer-state/server-transfer-state.ts b/ClientApp/modules/transfer-state/server-transfer-state.ts deleted file mode 100644 index b2890b26..00000000 --- a/ClientApp/modules/transfer-state/server-transfer-state.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Injectable, Optional, RendererFactory2, ViewEncapsulation, Inject, PLATFORM_ID } from '@angular/core'; -import { TransferState } from './transfer-state'; -import { PlatformState } from '@angular/platform-server'; -@Injectable() -export class ServerTransferState extends TransferState { - constructor(private state: PlatformState, private rendererFactory: RendererFactory2) { - super(); - } - - /** - * Inject the State into the bottom of the - */ - inject() { - try { - const document: any = this.state.getDocument(); - const transferStateString = JSON.stringify(this.toJson()); - const renderer = this.rendererFactory.createRenderer(document, { - id: '-1', - encapsulation: ViewEncapsulation.None, - styles: [], - data: {} - }); - - const body = document.body; - - const script = renderer.createElement('script'); - renderer.setValue(script, `window['TRANSFER_STATE'] = ${transferStateString}`); - renderer.appendChild(body, script); - } catch (e) { - console.log('Failed to append TRANSFER_STATE to body'); - console.error(e); - } - } - - -} diff --git a/ClientApp/modules/transfer-state/transfer-state.ts b/ClientApp/modules/transfer-state/transfer-state.ts deleted file mode 100644 index cc963b8f..00000000 --- a/ClientApp/modules/transfer-state/transfer-state.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Injectable, Inject, PLATFORM_ID } from '@angular/core'; - -@Injectable() -export class TransferState { - private _map = new Map(); - - constructor() { } - - keys() { - return this._map.keys(); - } - - get(key: string): any { - const cachedValue = this._map.get(key); - this._map.delete(key); - return cachedValue; - } - - set(key: string, value: any): Map { - return this._map.set(key, value); - } - - toJson(): any { - const obj = {}; - Array.from(this.keys()) - .forEach(key => { - obj[key] = this.get(key); - }); - return obj; - } - - initialize(obj: any): void { - Object.keys(obj) - .forEach(key => { - this.set(key, obj[key]); - }); - } - - inject(): void { } -} diff --git a/ClientApp/polyfills/temporary-aspnetcore-engine.ts b/ClientApp/polyfills/temporary-aspnetcore-engine.ts deleted file mode 100644 index d3157a1b..00000000 --- a/ClientApp/polyfills/temporary-aspnetcore-engine.ts +++ /dev/null @@ -1,254 +0,0 @@ -/* ********* TEMPORARILY HERE ************** - * - will be on npm soon - - * import { ngAspnetCoreEngine } from `@nguniversal/aspnetcore-engine`; - */ -import { Type, NgModuleFactory, NgModuleRef, ApplicationRef, Provider, CompilerFactory, Compiler } from '@angular/core'; -import { platformServer, platformDynamicServer, PlatformState, INITIAL_CONFIG, renderModuleFactory } from '@angular/platform-server'; -import { ResourceLoader } from '@angular/compiler'; -import * as fs from 'fs'; - -import { REQUEST } from '../app/shared/constants/request'; -import { ORIGIN_URL } from '../app/shared/constants/baseurl.constants'; - -export function createTransferScript(transferData: Object): string { - return ``; -} - -export class FileLoader implements ResourceLoader { - get(url: string): Promise { - return new Promise((resolve, reject) => { - fs.readFile(url, (err: NodeJS.ErrnoException, buffer: Buffer) => { - if (err) { - return reject(err); - } - - resolve(buffer.toString()); - }); - }); - } -} - -export interface IRequestParams { - location: any; // e.g., Location object containing information '/some/path' - origin: string; // e.g., 'https://example.com:1234/' - url: string; // e.g., '/some/path' - baseUrl: string; // e.g., '' or '/myVirtualDir' - absoluteUrl: string; // e.g., 'https://example.com:1234/some/path' - domainTasks: Promise; - data: any; // any custom object passed through from .NET -} - -export interface IEngineOptions { - appSelector: string; - request: IRequestParams; - ngModule: Type<{}> | NgModuleFactory<{}>; - providers?: Provider[]; -}; - -export function ngAspnetCoreEngine( - options: IEngineOptions -): Promise<{ html: string, globals: { styles: string, title: string, meta: string, transferData?: {}, [key: string]: any } }> { - - options.providers = options.providers || []; - - const compilerFactory: CompilerFactory = platformDynamicServer().injector.get(CompilerFactory); - const compiler: Compiler = compilerFactory.createCompiler([ - { - providers: [ - { provide: ResourceLoader, useClass: FileLoader } - ] - } - ]); - - return new Promise((resolve, reject) => { - - try { - const moduleOrFactory = options.ngModule; - if (!moduleOrFactory) { - throw new Error('You must pass in a NgModule or NgModuleFactory to be bootstrapped'); - } - - const extraProviders = options.providers.concat( - options.providers, - [ - { - provide: INITIAL_CONFIG, - useValue: { - document: options.appSelector, - url: options.request.url - } - }, - { - provide: ORIGIN_URL, - useValue: options.request.origin - }, { - provide: REQUEST, - useValue: options.request.data.request - } - ] - ); - - const platform = platformServer(extraProviders); - - getFactory(moduleOrFactory, compiler) - .then((factory: NgModuleFactory<{}>) => { - - return platform.bootstrapModuleFactory(factory).then((moduleRef: NgModuleRef<{}>) => { - - const state: PlatformState = moduleRef.injector.get(PlatformState); - const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef); - - appRef.isStable - .filter((isStable: boolean) => isStable) - .first() - .subscribe((stable) => { - - // Fire the TransferState Cache - const bootstrap = moduleRef.instance['ngOnBootstrap']; - bootstrap && bootstrap(); - - // The parse5 Document itself - const AST_DOCUMENT = state.getDocument(); - - // Strip out the Angular application - const htmlDoc = state.renderToString(); - - const APP_HTML = htmlDoc.substring( - htmlDoc.indexOf('') + 6, - htmlDoc.indexOf('') - ); - - // Strip out Styles / Meta-tags / Title - const STYLES = []; - const SCRIPTS = []; - const META = []; - const LINKS = []; - let TITLE = ''; - - let STYLES_STRING: string = htmlDoc.indexOf('') + 8) - : null; - - const HEAD = AST_DOCUMENT.head; - - let count = 0; - - for (let i = 0; i < HEAD.children.length; i++) { - let element = HEAD.children[i]; - - if (element.name === 'title') { - TITLE = element.children[0].data; - } - - if (element.name === 'script') { - SCRIPTS.push( - `` - ); - } - - // Broken after 4.0 (worked in rc) - // if (element.name === 'style') { - // let styleTag = '`; - // STYLES.push(styleTag); - // } - - if (element.name === 'meta') { - count = count + 1; - let metaString = '\n`); - } - - if (element.name === 'link') { - let linkString = '\n`); - } - } - - resolve({ - html: APP_HTML, - globals: { - styles: STYLES_STRING, - title: TITLE, - scripts: SCRIPTS.join(' '), - meta: META.join(' '), - links: LINKS.join(' ') - } - }); - - moduleRef.destroy(); - - }, (err) => { - // isStable subscription error (Template / code error) - reject(err); - }); - - }, err => { - // bootstrapModuleFactory error - reject(err); - }); - - }, err => { - // getFactory error - reject(err); - }); - - } catch (ex) { - // try/catch error - reject(ex); - } - - }); - -} - -/* ********************** Private / Internal ****************** */ - -const factoryCacheMap = new Map, NgModuleFactory<{}>>(); -function getFactory( - moduleOrFactory: Type<{}> | NgModuleFactory<{}>, compiler: Compiler -): Promise> { - - return new Promise>((resolve, reject) => { - // If module has been compiled AoT - if (moduleOrFactory instanceof NgModuleFactory) { - resolve(moduleOrFactory); - return; - } else { - let moduleFactory = factoryCacheMap.get(moduleOrFactory); - - // If module factory is cached - if (moduleFactory) { - resolve(moduleFactory); - return; - } - - // Compile the module and cache it - compiler.compileModuleAsync(moduleOrFactory) - .then((factory) => { - factoryCacheMap.set(moduleOrFactory, factory); - resolve(factory); - }, (err => { - reject(err); - })); - } - }); -} diff --git a/ClientApp/tsconfig.app.json b/ClientApp/tsconfig.app.json index 5e2507db..3f094c64 100644 --- a/ClientApp/tsconfig.app.json +++ b/ClientApp/tsconfig.app.json @@ -8,6 +8,7 @@ }, "exclude": [ "test.ts", - "**/*.spec.ts" + "**/*.spec.ts", + "boot.server.PRODUCTION.ts" ] } diff --git a/README.md b/README.md index e5578667..9dd99499 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,27 @@ -# ASP.NET Core 2.0 & Angular 4 (+) advanced starter - with Server-side prerendering (for Angular SEO)! +# ASP.NET Core 2.0 & Angular 5(+) Advanced Starter - with Server-side prerendering (for Angular SEO)! -> [(upcoming) Angular 5.0 demo Branch Here](https://github.com/MarkPieszak/aspnetcore-angular2-universal/tree/angular-5.0-WIP) +## By [DevHelp.Online](http://www.DevHelp.Online) + +> Updated to the latest Angular 5.x + +> Note ServerTransferModule still in the works - fix coming soon

- ASP.NET Core 2.0 Angular 4+ Starter + ASP.NET Core 2.0 Angular 5+ Starter

-### Harness the power of Angular 4+, ASP.NET Core 2.0, now with SEO ! +### Harness the power of Angular 5+, ASP.NET Core 2.0, now with SEO ! Angular SEO in action:

- ASP.NET Core Angular4 SEO + ASP.NET Core Angular5 SEO

### What is this repo? Live Demo here: http://aspnetcore-angular2-universal.azurewebsites.net This repository is maintained by [Angular](https://github.com/angular/angular) and is meant to be an advanced starter -for both ASP.NET Core 2.0 using Angular 4.0+, not only for the client-side, but to be rendered on the server for instant +for both ASP.NET Core 2.0 using Angular 5.0+, not only for the client-side, but to be rendered on the server for instant application paints (Note: If you don't need SSR [read here](#faq) on how to disable it). This is meant to be a Feature-Rich Starter application containing all of the latest technologies, best build systems available, and include many real-world examples and libraries needed in todays Single Page Applications (SPAs). @@ -51,7 +55,7 @@ This utilizes all the latest standards, no gulp, no bower, no typings, no manual - Swagger WebAPI documentation when running in development mode - SignalR Chat demo! (Thanks to [@hakonamatata](https://github.com/hakonamatata)) -- **Angular 4.0.0** : +- **Angular 5.0.0** : - (Minimal) Angular-CLI integration - This is to be used mainly for Generating Components/Services/etc. - Usage examples: @@ -139,9 +143,8 @@ export ASPNETCORE_ENVIRONMENT=Development # Upcoming Features: -- Update to use npm [ngAspnetCoreEngine](https://github.com/angular/universal/pull/682) (still need to tweak a few things there) -- Potractor e2e testing -- Add basic Redux State store (Will also hold state durijg HMR builds) +- Fix HttpTransferCacheModule & ServerTransferModule to work with aspnet-engine +- ~~Update to use npm [ngAspnetCoreEngine](https://github.com/angular/universal/pull/682) (still need to tweak a few things there)~~ ---- @@ -268,7 +271,7 @@ Angular application gets serialized into a String, sent to the Browser, along wi The short-version is that we invoke that Node process, passing in our Request object & invoke the `boot.server` file, and we get back a nice object that we pass into .NETs `ViewData` object, and sprinkle through out our `Views/Shared/_Layout.cshtml` and `/Views/Home/index.cshtml` files! -A more detailed explanation can be found here: [ng-AspnetCore-Engine Readme](https://github.com/angular/universal/tree/master/modules/ng-aspnetcore-engine) +A more detailed explanation can be found here: [ng-AspnetCore-Engine Readme](https://github.com/angular/universal/tree/master/modules/aspnetcore-engine) ```csharp // Prerender / Serialize application @@ -293,7 +296,7 @@ Take a look at the `_Layout.cshtml` file for example, notice how we let .NET han - @ViewData["Title"] - AspNET.Core Angular 4.0.0 (+) starter + @ViewData["Title"] - AspNET.Core Angular 5.0.0 (+) starter @@ -334,7 +337,7 @@ Well now, your Client-side Angular will take over, and you'll have a fully funct - This repository uses ASP.Net Core 2.0, which has a hard requirement on .NET Core Runtime 2.0.0 and .NET Core SDK 2.0.0. Please install these items from [here](https://github.com/dotnet/core/blob/master/release-notes/download-archives/2.0.0-download.md) -> When building components in Angular 4 there are a few things to keep in mind. +> When building components in Angular 5 there are a few things to keep in mind. - **`window`**, **`document`**, **`navigator`**, and other browser types - _do not exist on the server_ - so using them, or any library that uses them (jQuery for example) will not work. You do have some options, if you truly need some of this functionality: - If you need to use them, consider limiting them to only your client and wrapping them situationally. You can use the Object injected using the PLATFORM_ID token to check whether the current platform is browser or server. @@ -379,7 +382,7 @@ constructor(element: ElementRef, renderer: Renderer2) { ### How can I disable SSR (Server-side rendering)? Simply comment out the logic within HomeController, and replace `@Html.Raw(ViewData["SpaHtml"])` with just your applications root -AppComponent tag ("app" in our case): ``. +AppComponent tag ("app-root" in our case): ``. > You could also remove any `isPlatformBrowser/etc` logic, and delete the boot.server, app.module.browser & app.module.server files, just make sure your `boot.browser` file points to `app.module`. @@ -395,7 +398,7 @@ This is now possible, with the recently updated Angular Material changes. We do ### How can I use jQuery and/or some jQuery plugins with this repo? > Note: If at all possible, try to avoid using jQuery or libraries dependent on it, as there are -better, more abstract ways of dealing with the DOM in Angular (4+) such as using the Renderer, etc. +better, more abstract ways of dealing with the DOM in Angular (5+) such as using the Renderer, etc. Yes, of course but there are a few things you need to setup before doing this. First, make sure jQuery is included in webpack vendor file, and that you have a webpack Plugin setup for it. `new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' })` @@ -447,10 +450,14 @@ Copyright (c) 2016-2017 [Mark Pieszak](https://github.com/MarkPieszak) # DevHelp.Online - Angular & ASP.NET - Consulting | Training | Development -Check out **[www.DevHelp.Online](http://DevHelp.Online)** for more info! +Check out **[www.DevHelp.Online](http://DevHelp.Online)** for more info! Twitter [@DevHelpOnline](http://www.twitter.com/DevHelpOnline) Contact us at , and let's talk about your projects needs. +

+ DevHelp.Online - Angular ASPNET JavaScript Consulting Development and Training +

+ ---- ## Follow me online: diff --git a/Server/Controllers/HomeController.cs b/Server/Controllers/HomeController.cs index 8a5a77f6..0a674a60 100644 --- a/Server/Controllers/HomeController.cs +++ b/Server/Controllers/HomeController.cs @@ -21,7 +21,7 @@ public async Task Index() { var prerenderResult = await Request.BuildPrerender(); - ViewData["SpaHtml"] = prerenderResult.Html; // our from Angular + ViewData["SpaHtml"] = prerenderResult.Html; // our from Angular ViewData["Title"] = prerenderResult.Globals["title"]; // set our from Angular ViewData["Styles"] = prerenderResult.Globals["styles"]; // put styles in the correct place ViewData["Scripts"] = prerenderResult.Globals["scripts"]; // scripts (that were in our header) diff --git a/Startup.cs b/Startup.cs index 7736f9e9..b7773ac0 100644 --- a/Startup.cs +++ b/Startup.cs @@ -54,7 +54,7 @@ public void ConfigureServices(IServiceCollection services) // Register the Swagger generator, defining one or more Swagger documents services.AddSwaggerGen(c => { - c.SwaggerDoc("v1", new Info { Title = "Angular 4.0 Universal & ASP.NET Core advanced starter-kit web API", Version = "v1" }); + c.SwaggerDoc("v1", new Info { Title = "Angular 5.0 Universal & ASP.NET Core advanced starter-kit web API", Version = "v1" }); }); } @@ -74,7 +74,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { HotModuleReplacement = true, - HotModuleReplacementEndpoint = "/dist/__webpack_hmr" + HotModuleReplacementEndpoint = "/dist/" }); app.UseSwagger(); app.UseSwaggerUI(c => diff --git a/Views/Home/Index.cshtml b/Views/Home/Index.cshtml index b0796562..8c18039d 100644 --- a/Views/Home/Index.cshtml +++ b/Views/Home/Index.cshtml @@ -1,4 +1,8 @@ -@Html.Raw(ViewData["SpaHtml"]) +<!-- Remove this if you want to remove Server-side rendering --> +@Html.Raw(ViewData["SpaHtml"]) + +<!-- if you only want Client-side rendering uncomment this --> +<!-- <app-root></app-root> --> <script src="http://github.com/~/dist/vendor.js" asp-append-version="true"></script> @section scripts { diff --git a/package.json b/package.json index 9ebbf620..b86fbe7f 100644 --- a/package.json +++ b/package.json @@ -11,24 +11,25 @@ "build:dev": "npm run build:vendor && npm run build:webpack", "build:webpack": "webpack --progress --color", "build:prod": "npm run clean && npm run build:vendor -- --env.prod && npm run build:webpack -- --env.prod", + "build:p": "npm run build:webpack -- --env.prod", "build:vendor": "webpack --config webpack.config.vendor.js --progress --color", "clean": "rimraf wwwroot/dist clientapp/dist" }, "dependencies": { - "@angular/animations": "^4.3.0", - "@angular/common": "^4.3.0", - "@angular/compiler": "^4.3.0", - "@angular/compiler-cli": "^4.3.0", - "@angular/core": "^4.3.0", - "@angular/forms": "^4.3.0", - "@angular/http": "^4.3.0", - "@angular/platform-browser": "^4.3.0", - "@angular/platform-browser-dynamic": "^4.3.0", - "@angular/platform-server": "^4.3.0", - "@angular/router": "^4.3.0", - "@nguniversal/aspnetcore-engine": "^1.0.0-beta.2", - "@ngx-translate/core": "^6.0.1", - "@ngx-translate/http-loader": "0.0.3", + "@angular/animations": "^5.0.0", + "@angular/common": "^5.0.0", + "@angular/compiler": "^5.0.0", + "@angular/core": "^5.0.0", + "@angular/forms": "^5.0.0", + "@angular/http": "^5.0.0", + "@angular/platform-browser": "^5.0.0", + "@angular/platform-browser-dynamic": "^5.0.0", + "@angular/platform-server": "^5.0.0", + "@angular/router": "^5.0.0", + "@nguniversal/aspnetcore-engine": "^5.0.0-beta.5", + "@nguniversal/common": "^5.0.0-beta.5", + "@ngx-translate/core": "^8.0.0", + "@ngx-translate/http-loader": "^2.0.0", "@types/node": "^7.0.12", "angular2-router-loader": "^0.3.5", "angular2-template-loader": "^0.6.2", @@ -49,16 +50,16 @@ "jquery": "^2.2.1", "json-loader": "^0.5.4", "moment": "2.18.1", - "ngx-bootstrap": "2.0.0-beta.3", + "ngx-bootstrap": "2.0.0-beta.6", "node-sass": "^4.5.2", "preboot": "^5.0.0", "raw-loader": "^0.5.1", "rimraf": "^2.6.2", - "rxjs": "^5.4.3", + "rxjs": "^5.5.6", "sass-loader": "^6.0.6", "style-loader": "^0.18.2", "to-string-loader": "^1.1.5", - "typescript": "2.5.2", + "typescript": "~2.5.0", "url-loader": "^0.5.7", "webpack": "^3.6.0", "webpack-hot-middleware": "^2.19.1", @@ -66,8 +67,9 @@ "zone.js": "^0.8.17" }, "devDependencies": { - "@angular/cli": "^1.3.2", - "@ngtools/webpack": "^1.3.0", + "@angular/cli": "^1.7.0-beta.1", + "@angular/compiler-cli": "^5.2.1", + "@ngtools/webpack": "^1.9.0", "@types/chai": "^3.4.34", "@types/jasmine": "^2.5.37", "chai": "^3.5.0", diff --git a/tslint.json b/tslint.json index 657d49b6..f318a0b8 100644 --- a/tslint.json +++ b/tslint.json @@ -1,4 +1,5 @@ { + "defaultSeverity": "warn", "rules": { "align": false, "ban": false, @@ -75,7 +76,7 @@ "no-switch-case-fall-through": true, "no-trailing-whitespace": false, "no-unused-expression": true, - "no-unused-variable": false, + "no-unused-variable": true, "no-use-before-declare": true, "no-var-keyword": true, "no-var-requires": false, diff --git a/webpack.config.js b/webpack.config.js index 02ff0a01..02ee879f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -11,7 +11,7 @@ const path = require('path'); const webpack = require('webpack'); const merge = require('webpack-merge'); -const AotPlugin = require('@ngtools/webpack').AotPlugin; +const AngularCompilerPlugin = require('@ngtools/webpack').AngularCompilerPlugin; const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin; const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; @@ -59,36 +59,60 @@ module.exports = (env) => { ] : [ // new BundleAnalyzerPlugin(), // Plugins that apply in production builds only - new webpack.optimize.UglifyJsPlugin(), - new AotPlugin({ - tsConfigPath: './tsconfig.json', - entryModule: path.join(__dirname, 'ClientApp/app/app.module.browser#AppModule'), - exclude: ['./**/*.server.ts'] - }) - ]) + new AngularCompilerPlugin({ + mainPath: path.join(__dirname, 'ClientApp/boot.browser.ts'), + tsConfigPath: './tsconfig.json', + entryModule: path.join(__dirname, 'ClientApp/app/app.module.browser#AppModule'), + exclude: ['./**/*.server.ts'] + }), + // new webpack.optimize.UglifyJsPlugin({ + // compress: false, + // mangle: false + // }) + ]), + devtool: isDevBuild ? 'cheap-eval-source-map' : false, + node: { + fs: "empty" + } }); // Configuration for server-side (prerendering) bundle suitable for running in Node const serverBundleConfig = merge(sharedConfig, { // resolve: { mainFields: ['main'] }, - entry: { 'main-server': './ClientApp/boot.server.ts' }, + entry: { + 'main-server': + isDevBuild ? './ClientApp/boot.server.ts' : './ClientApp/boot.server.PRODUCTION.ts' + }, plugins: [ new webpack.DllReferencePlugin({ context: __dirname, manifest: require('./ClientApp/dist/vendor-manifest.json'), sourceType: 'commonjs2', name: './vendor' - }) - ].concat(isDevBuild ? [] : [ - new webpack.optimize.UglifyJsPlugin({ - compress: false, - mangle: false }), + new webpack.ContextReplacementPlugin( + // fixes WARNING Critical dependency: the request of a dependency is an expression + /(.+)?angular(\\|\/)core(.+)?/, + path.join(__dirname, 'src'), // location of your src + {} // a map of your routes + ), + new webpack.ContextReplacementPlugin( + // fixes WARNING Critical dependency: the request of a dependency is an expression + /(.+)?express(\\|\/)(.+)?/, + path.join(__dirname, 'src'), + {} + ) + ].concat(isDevBuild ? [] : [ + // new webpack.optimize.UglifyJsPlugin({ + // compress: false, + // mangle: false + // }), // Plugins that apply in production builds only - new AotPlugin({ - tsConfigPath: './tsconfig.json', - entryModule: path.join(__dirname, 'ClientApp/app/app.module.server#AppModule'), - exclude: ['./**/*.browser.ts'] + new AngularCompilerPlugin({ + mainPath: path.join(__dirname, 'ClientApp/boot.server.PRODUCTION.ts'), + tsConfigPath: './tsconfig.json', + entryModule: path.join(__dirname, 'ClientApp/app/app.module.server#AppModule'), + exclude: ['./**/*.browser.ts'] }) ]), output: { @@ -96,7 +120,8 @@ module.exports = (env) => { path: path.join(__dirname, './ClientApp/dist') }, target: 'node', - devtool: isDevBuild ? 'inline-source-map': false + // switch to "inline-source-map" if you want to debug the TS during SSR + devtool: isDevBuild ? 'cheap-eval-source-map' : false }); return [clientBundleConfig, serverBundleConfig]; diff --git a/webpack.config.vendor.js b/webpack.config.vendor.js index 3e07b78b..6d35d3ba 100644 --- a/webpack.config.vendor.js +++ b/webpack.config.vendor.js @@ -46,7 +46,7 @@ module.exports = (env) => { 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.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.ContextReplacementPlugin(/(.+)?angular(\\|\/)core(.+)?/, 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 ] }; From 7252a4524bd83a3256883607bbc090cb2280b599 Mon Sep 17 00:00:00 2001 From: Mark Pieszak <mpieszak84@gmail.com> Date: Tue, 23 Jan 2018 15:24:42 -0500 Subject: [PATCH 02/60] docs(readme): gotchas --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9dd99499..8cffbc2f 100644 --- a/README.md +++ b/README.md @@ -339,6 +339,10 @@ Well now, your Client-side Angular will take over, and you'll have a fully funct > When building components in Angular 5 there are a few things to keep in mind. + - Make sure you provide Absolute URLs when calling any APIs. (The server can't understand relative paths, so `/api/whatever` will fail). + + - API calls will be ran during a server, and once again during the client render, so make sure you're using transfering data that's important to you so that you don't see a flicker. + - **`window`**, **`document`**, **`navigator`**, and other browser types - _do not exist on the server_ - so using them, or any library that uses them (jQuery for example) will not work. You do have some options, if you truly need some of this functionality: - If you need to use them, consider limiting them to only your client and wrapping them situationally. You can use the Object injected using the PLATFORM_ID token to check whether the current platform is browser or server. @@ -444,7 +448,7 @@ Nothing's ever perfect, but please let me know by creating an issue (make sure t [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](/LICENSE) -Copyright (c) 2016-2017 [Mark Pieszak](https://github.com/MarkPieszak) +Copyright (c) 2016-2018 [Mark Pieszak](https://github.com/MarkPieszak) ---- From 45d6eaaa1063af25255a7d633524ed06f965471f Mon Sep 17 00:00:00 2001 From: Luiz Machado <machado@odahcam.com> Date: Thu, 25 Jan 2018 13:31:17 -0200 Subject: [PATCH 03/60] fix(webpack): moved context replacement plugin to dev builds only (#545) closes #543 --- webpack.config.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 02ee879f..6c348162 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -89,7 +89,8 @@ module.exports = (env) => { manifest: require('./ClientApp/dist/vendor-manifest.json'), sourceType: 'commonjs2', name: './vendor' - }), + }) + ].concat(isDevBuild ? [ new webpack.ContextReplacementPlugin( // fixes WARNING Critical dependency: the request of a dependency is an expression /(.+)?angular(\\|\/)core(.+)?/, @@ -102,7 +103,7 @@ module.exports = (env) => { path.join(__dirname, 'src'), {} ) - ].concat(isDevBuild ? [] : [ + ] : [ // new webpack.optimize.UglifyJsPlugin({ // compress: false, // mangle: false From b7dd580acff001996ded91a13a29aa44a34eb2f0 Mon Sep 17 00:00:00 2001 From: Jan Sviland <jan@sviland.org> Date: Thu, 25 Jan 2018 16:31:52 +0100 Subject: [PATCH 04/60] browser caching (#542) closes #541 --- Startup.cs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Startup.cs b/Startup.cs index b7773ac0..db1d5d8d 100644 --- a/Startup.cs +++ b/Startup.cs @@ -9,6 +9,8 @@ using Microsoft.EntityFrameworkCore; using AspCoreServer.Data; using Swashbuckle.AspNetCore.Swagger; +using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; namespace AspCoreServer { @@ -64,7 +66,30 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); - app.UseStaticFiles(); + // app.UseStaticFiles(); + + app.UseStaticFiles(new StaticFileOptions() + { + OnPrepareResponse = c => + { + //Do not add cache to json files. We need to have new versions when we add new translations. + + if (!c.Context.Request.Path.Value.Contains(".json")) + { + c.Context.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue() + { + MaxAge = TimeSpan.FromDays(30) // Cache everything except json for 30 days + }; + } + else + { + c.Context.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue() + { + MaxAge = TimeSpan.FromMinutes(15) // Cache json for 15 minutes + }; + } + } + }); DbInitializer.Initialize(context); From c8f3a1e5606f2e5115adf2b4dc6fc62d6b625380 Mon Sep 17 00:00:00 2001 From: Eugen Tatuev <eugentatuev@gmail.com> Date: Tue, 30 Jan 2018 08:33:03 +0300 Subject: [PATCH 05/60] fix(webpack): enable uglifyjs for non-vendor bundles --- webpack.config.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 6c348162..a54d3135 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -65,10 +65,11 @@ module.exports = (env) => { entryModule: path.join(__dirname, 'ClientApp/app/app.module.browser#AppModule'), exclude: ['./**/*.server.ts'] }), - // new webpack.optimize.UglifyJsPlugin({ - // compress: false, - // mangle: false - // }) + new webpack.optimize.UglifyJsPlugin({ + output: { + ascii_only: true, + } + }), ]), devtool: isDevBuild ? 'cheap-eval-source-map' : false, node: { @@ -104,10 +105,13 @@ module.exports = (env) => { {} ) ] : [ - // new webpack.optimize.UglifyJsPlugin({ - // compress: false, - // mangle: false - // }), + new webpack.optimize.UglifyJsPlugin({ + mangle: false, + compress: false, + output: { + ascii_only: true, + } + }), // Plugins that apply in production builds only new AngularCompilerPlugin({ mainPath: path.join(__dirname, 'ClientApp/boot.server.PRODUCTION.ts'), From 0ad0f60766bc08f3a1e2f36fc2944db874b44b9d Mon Sep 17 00:00:00 2001 From: Isaac Levin <isaac.r.levin@gmail.com> Date: Mon, 5 Feb 2018 12:25:45 -0500 Subject: [PATCH 06/60] Update to REST Api to better handle PUT/DELETE --- .../user-detail/user-detail.component.ts | 7 +- .../app/containers/users/users.component.html | 4 +- .../app/containers/users/users.component.ts | 120 ++++++++++-------- Server/RestAPI/UsersController.cs | 6 +- 4 files changed, 75 insertions(+), 62 deletions(-) diff --git a/ClientApp/app/components/user-detail/user-detail.component.ts b/ClientApp/app/components/user-detail/user-detail.component.ts index d21d3415..75a8369e 100644 --- a/ClientApp/app/components/user-detail/user-detail.component.ts +++ b/ClientApp/app/components/user-detail/user-detail.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, Output, EventEmitter } from '@angular/core'; import { IUser } from '../../models/User'; import { UserService } from '../../shared/user.service'; @@ -8,7 +8,7 @@ import { UserService } from '../../shared/user.service'; }) export class UserDetailComponent { @Input() user: IUser; - + @Output() userUpdate: EventEmitter<any> = new EventEmitter(); constructor(private userService: UserService) { } @@ -17,6 +17,7 @@ export class UserDetailComponent { console.log('Put user result: ', result); }, error => { console.log(`There was an issue. ${error._body}.`); - }); + }); + this.userUpdate.emit(null); } } diff --git a/ClientApp/app/containers/users/users.component.html b/ClientApp/app/containers/users/users.component.html index 1e661476..b90c06aa 100644 --- a/ClientApp/app/containers/users/users.component.html +++ b/ClientApp/app/containers/users/users.component.html @@ -1,4 +1,4 @@ -<h1>This is a RestAPI Example (hitting WebAPI in our case)</h1> +<h1>This is a RestAPI Example (hitting WebAPI in our case)</h1> <blockquote> Let's get some fake users from Rest:<br> @@ -26,4 +26,4 @@ <h2>Users</h2> </button> </li> </ul> -<app-user-detail [user]="selectedUser"></app-user-detail> +<app-user-detail (userUpdate)="onUserUpdate($event)" [user]="selectedUser"></app-user-detail> diff --git a/ClientApp/app/containers/users/users.component.ts b/ClientApp/app/containers/users/users.component.ts index 35cc8517..6bca6a73 100644 --- a/ClientApp/app/containers/users/users.component.ts +++ b/ClientApp/app/containers/users/users.component.ts @@ -1,69 +1,81 @@ -import { - Component, OnInit, - // animation imports - trigger, state, style, transition, animate, Inject +import { + Component, OnInit, + // animation imports + trigger, state, style, transition, animate, Inject } from '@angular/core'; import { IUser } from '../../models/User'; import { UserService } from '../../shared/user.service'; @Component({ - selector: 'app-users', - templateUrl: './users.component.html', - styleUrls: ['./users.component.css'], - animations: [ - // Animation example - // Triggered in the ngFor with [@flyInOut] - trigger('flyInOut', [ - state('in', style({ transform: 'translateY(0)' })), - transition('void => *', [ - style({ transform: 'translateY(-100%)' }), - animate(1000) - ]), - transition('* => void', [ - animate(1000, style({ transform: 'translateY(100%)' })) - ]) - ]) - ] + selector: 'app-users', + templateUrl: './users.component.html', + styleUrls: ['./users.component.css'], + animations: [ + // Animation example + // Triggered in the ngFor with [@flyInOut] + trigger('flyInOut', [ + state('in', style({ transform: 'translateY(0)' })), + transition('void => *', [ + style({ transform: 'translateY(-100%)' }), + animate(1000) + ]), + transition('* => void', [ + animate(1000, style({ transform: 'translateY(100%)' })) + ]) + ]) + ] }) export class UsersComponent implements OnInit { - users: IUser[]; - selectedUser: IUser; + users: IUser[]; + selectedUser: IUser; - // Use "constructor"s only for dependency injection - constructor( - private userService: UserService - ) { } + // Use "constructor"s only for dependency injection + constructor( + private userService: UserService + ) { } - // Here you want to handle anything with @Input()'s @Output()'s - // Data retrieval / etc - this is when the Component is "ready" and wired up - ngOnInit() { - this.userService.getUsers().subscribe(result => { - console.log('HttpClient [GET] /api/users/allresult', result); - this.users = result; - }); - } + // Here you want to handle anything with @Input()'s @Output()'s + // Data retrieval / etc - this is when the Component is "ready" and wired up + ngOnInit() { + this.userService.getUsers().subscribe(result => { + console.log('HttpClient [GET] /api/users/allresult', result); + this.users = result; + }); + } - onSelect(user: IUser): void { - this.selectedUser = user; - } + onSelect(user: IUser): void { + this.selectedUser = user; + } - deleteUser(user) { - this.userService.deleteUser(user).subscribe(result => { - console.log('Delete user result: ', result); - let position = this.users.indexOf(user); - this.users.splice(position, 1); - }, error => { - console.log(`There was an issue. ${error._body}.`); - }); - } + deleteUser(user) { + this.clearUser(); + this.userService.deleteUser(user).subscribe(result => { + console.log('Delete user result: ', result); + let position = this.users.indexOf(user); + this.users.splice(position, 1); + }, error => { + console.log(`There was an issue. ${error._body}.`); + }); + } + + onUserUpdate(event) { + this.clearUser(); + } + + addUser(newUserName) { + this.clearUser(); + this.userService.addUser(newUserName).subscribe(result => { + console.log('Post user result: ', result); + this.users.push(result); + }, error => { + console.log(`There was an issue. ${error._body}.`); + }); + } - addUser(newUserName) { - this.userService.addUser(newUserName).subscribe(result => { - console.log('Post user result: ', result); - this.users.push(result); - }, error => { - console.log(`There was an issue. ${error._body}.`); - }); + clearUser() { + if (this.selectedUser) { + this.selectedUser = null; } + } } diff --git a/Server/RestAPI/UsersController.cs b/Server/RestAPI/UsersController.cs index 2938eb8e..3a7fe3d2 100644 --- a/Server/RestAPI/UsersController.cs +++ b/Server/RestAPI/UsersController.cs @@ -1,4 +1,4 @@ -using AspCoreServer.Data; +using AspCoreServer.Data; using AspCoreServer.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -89,7 +89,7 @@ public async Task<IActionResult> Put(int id, [FromBody]User userUpdateValue) { _context.Update(userUpdateValue); await _context.SaveChangesAsync(); - return Ok("Updated user - " + userUpdateValue.Name); + return Json("Updated user - " + userUpdateValue.Name); } } catch (DbUpdateException) @@ -116,7 +116,7 @@ public async Task<IActionResult> Delete(int id) { _context.User.Remove(userToRemove); await _context.SaveChangesAsync(); - return Ok("Deleted user - " + userToRemove.Name); + return Json("Deleted user - " + userToRemove.Name); } } } From 0f2609c93aaad591d73db99acc7e7957c5051bd8 Mon Sep 17 00:00:00 2001 From: peterdobson <petedobson@gmail.com> Date: Wed, 7 Mar 2018 03:16:04 +1100 Subject: [PATCH 07/60] fix(sitemap): update homecontroller sitemap tags Renamed the "sitemapindex" & "sitemap" SitemapXml tags to "urlset" & "url" to allow Google Search Console to process sitemap.xml without errors closes #562 --- Server/Controllers/HomeController.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Server/Controllers/HomeController.cs b/Server/Controllers/HomeController.cs index 0a674a60..bd9ad008 100644 --- a/Server/Controllers/HomeController.cs +++ b/Server/Controllers/HomeController.cs @@ -38,16 +38,16 @@ public async Task<IActionResult> SitemapXml() { String xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"; - xml += "<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9/">"; - xml += "<sitemap>"; + xml += "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9/">"; + xml += "<url>"; xml += "<loc>http://localhost:4251/home</loc>"; xml += "<lastmod>" + DateTime.Now.ToString("yyyy-MM-dd") + "</lastmod>"; - xml += "</sitemap>"; - xml += "<sitemap>"; + xml += "</url>"; + xml += "<url>"; xml += "<loc>http://localhost:4251/counter</loc>"; xml += "<lastmod>" + DateTime.Now.ToString("yyyy-MM-dd") + "</lastmod>"; - xml += "</sitemap>"; - xml += "</sitemapindex>"; + xml += "</url>"; + xml += "</urlset>"; return Content(xml, "text/xml"); From fdef986e352f980bf9091458fd7a1dd2dd8ec0f5 Mon Sep 17 00:00:00 2001 From: Mark Pieszak <mpieszak84@gmail.com> Date: Thu, 22 Mar 2018 15:08:45 -0400 Subject: [PATCH 08/60] fix(nguniversal): pin to beta.5 until new release --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b86fbe7f..d2adcd26 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ "@angular/platform-browser-dynamic": "^5.0.0", "@angular/platform-server": "^5.0.0", "@angular/router": "^5.0.0", - "@nguniversal/aspnetcore-engine": "^5.0.0-beta.5", - "@nguniversal/common": "^5.0.0-beta.5", + "@nguniversal/aspnetcore-engine": "5.0.0-beta.5", + "@nguniversal/common": "5.0.0-beta.5", "@ngx-translate/core": "^8.0.0", "@ngx-translate/http-loader": "^2.0.0", "@types/node": "^7.0.12", From dfa802628d895cfbff61ccfb5c4556e50860fb42 Mon Sep 17 00:00:00 2001 From: CaerusKaru <caerus.karu@gmail.com> Date: Fri, 23 Mar 2018 18:40:24 -0400 Subject: [PATCH 09/60] chore: update to latest nguniversal and fix imports (#578) closes #577 --- ClientApp/app/app.component.ts | 2 +- ClientApp/app/app.module.browser.ts | 2 +- ClientApp/app/app.module.ts | 2 +- ClientApp/app/shared/user.service.ts | 2 +- package.json | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ClientApp/app/app.component.ts b/ClientApp/app/app.component.ts index 7a6a75ed..d8b28150 100644 --- a/ClientApp/app/app.component.ts +++ b/ClientApp/app/app.component.ts @@ -7,7 +7,7 @@ import { LinkService } from './shared/link.service'; // i18n support import { TranslateService } from '@ngx-translate/core'; -import { REQUEST } from '@nguniversal/aspnetcore-engine'; +import { REQUEST } from '@nguniversal/aspnetcore-engine/tokens'; @Component({ selector: 'app-root', diff --git a/ClientApp/app/app.module.browser.ts b/ClientApp/app/app.module.browser.ts index 56d1c20c..fa983b67 100644 --- a/ClientApp/app/app.module.browser.ts +++ b/ClientApp/app/app.module.browser.ts @@ -3,7 +3,7 @@ import { BrowserModule } from '@angular/platform-browser'; import { APP_BASE_HREF } from '@angular/common'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { ORIGIN_URL, REQUEST } from '@nguniversal/aspnetcore-engine'; +import { ORIGIN_URL, REQUEST } from '@nguniversal/aspnetcore-engine/tokens'; import { AppModuleShared } from './app.module'; import { AppComponent } from './app.component'; import { BrowserTransferStateModule } from '@angular/platform-browser'; diff --git a/ClientApp/app/app.module.ts b/ClientApp/app/app.module.ts index d31250c4..5394d67d 100644 --- a/ClientApp/app/app.module.ts +++ b/ClientApp/app/app.module.ts @@ -24,7 +24,7 @@ import { NgxBootstrapComponent } from './containers/ngx-bootstrap-demo/ngx-boots import { LinkService } from './shared/link.service'; import { UserService } from './shared/user.service'; -import { ORIGIN_URL } from '@nguniversal/aspnetcore-engine'; +import { ORIGIN_URL } from '@nguniversal/aspnetcore-engine/tokens'; export function createTranslateLoader(http: HttpClient, baseHref) { // Temporary Azure hack diff --git a/ClientApp/app/shared/user.service.ts b/ClientApp/app/shared/user.service.ts index 9489e32f..6a3d7cb7 100644 --- a/ClientApp/app/shared/user.service.ts +++ b/ClientApp/app/shared/user.service.ts @@ -2,7 +2,7 @@ import { HttpClient } from '@angular/common/http'; import { Http, URLSearchParams } from '@angular/http'; import { APP_BASE_HREF } from '@angular/common'; -import { ORIGIN_URL } from '@nguniversal/aspnetcore-engine'; +import { ORIGIN_URL } from '@nguniversal/aspnetcore-engine/tokens'; import { IUser } from '../models/User'; import { Observable } from 'rxjs/Observable'; diff --git a/package.json b/package.json index d2adcd26..86dacd20 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ "@angular/platform-browser-dynamic": "^5.0.0", "@angular/platform-server": "^5.0.0", "@angular/router": "^5.0.0", - "@nguniversal/aspnetcore-engine": "5.0.0-beta.5", - "@nguniversal/common": "5.0.0-beta.5", + "@nguniversal/aspnetcore-engine": "^5.0.0-beta.8", + "@nguniversal/common": "^5.0.0-beta.8", "@ngx-translate/core": "^8.0.0", "@ngx-translate/http-loader": "^2.0.0", "@types/node": "^7.0.12", From 3a8bfe242a31caba034f55162d9f1be972f406ab Mon Sep 17 00:00:00 2001 From: Rune Antonsen <rune.antonsen@gmail.com> Date: Tue, 27 Mar 2018 14:21:40 +0200 Subject: [PATCH 10/60] fix(polyfills): move reflect-metadata to polyfills.ts (#581) No need to have it in both browser.polyfills.ts and server.polyfills.ts when it's the same. Closes #580 --- ClientApp/polyfills/browser.polyfills.ts | 1 - ClientApp/polyfills/polyfills.ts | 3 +++ ClientApp/polyfills/server.polyfills.ts | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ClientApp/polyfills/browser.polyfills.ts b/ClientApp/polyfills/browser.polyfills.ts index fa02ffab..dd813a13 100644 --- a/ClientApp/polyfills/browser.polyfills.ts +++ b/ClientApp/polyfills/browser.polyfills.ts @@ -1,4 +1,3 @@ import './polyfills.ts'; import 'zone.js/dist/zone'; -import 'reflect-metadata'; diff --git a/ClientApp/polyfills/polyfills.ts b/ClientApp/polyfills/polyfills.ts index fe3a6bd6..39ccb969 100644 --- a/ClientApp/polyfills/polyfills.ts +++ b/ClientApp/polyfills/polyfills.ts @@ -18,6 +18,9 @@ import 'core-js/es6/map'; import 'core-js/es6/set'; + /** */ + import 'reflect-metadata'; + /** IE10 and IE11 requires the following for NgClass support on SVG elements */ // import 'classlist.js'; // Run `npm install --save classlist.js`. diff --git a/ClientApp/polyfills/server.polyfills.ts b/ClientApp/polyfills/server.polyfills.ts index 7d3dc3bf..7044895f 100644 --- a/ClientApp/polyfills/server.polyfills.ts +++ b/ClientApp/polyfills/server.polyfills.ts @@ -1,4 +1,3 @@ import './polyfills.ts'; -import 'reflect-metadata'; import 'zone.js'; From 3269cf0ef6bd4b6aad3357cbdb472b1ca07dc4f0 Mon Sep 17 00:00:00 2001 From: Rune Antonsen <rune.antonsen@gmail.com> Date: Tue, 27 Mar 2018 14:46:57 +0200 Subject: [PATCH 11/60] fix(polyfills): web-animations-js should be in browser.polyfills.ts (#582) --- ClientApp/polyfills/browser.polyfills.ts | 2 ++ ClientApp/polyfills/polyfills.ts | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ClientApp/polyfills/browser.polyfills.ts b/ClientApp/polyfills/browser.polyfills.ts index dd813a13..1db98ac8 100644 --- a/ClientApp/polyfills/browser.polyfills.ts +++ b/ClientApp/polyfills/browser.polyfills.ts @@ -1,3 +1,5 @@ import './polyfills.ts'; import 'zone.js/dist/zone'; +import 'reflect-metadata'; +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. diff --git a/ClientApp/polyfills/polyfills.ts b/ClientApp/polyfills/polyfills.ts index 39ccb969..4382a0e1 100644 --- a/ClientApp/polyfills/polyfills.ts +++ b/ClientApp/polyfills/polyfills.ts @@ -24,8 +24,6 @@ /** IE10 and IE11 requires the following for NgClass support on SVG elements */ // import 'classlist.js'; // Run `npm install --save classlist.js`. -// import 'web-animations-js'; // Run `npm install --save web-animations-js`. - /** Evergreen browsers require these. **/ import 'core-js/es6/reflect'; import 'core-js/es7/reflect'; From 82afbb5781165081e6167e4679a89ed1caeba8b6 Mon Sep 17 00:00:00 2001 From: Flood <linusflood@gmail.com> Date: Fri, 6 Apr 2018 15:25:58 +0200 Subject: [PATCH 12/60] fix(cache): add cache busting to js files This fix makes the cache busting work. When using asp-append-version="true" in Index.cshtml like this: <script src="http://github.com/~/dist/vendor.js" asp-append-version="true"></script> It will turn that into: <script src="http://github.com/~/dist/vendor.js?v=37182361827361" ></script> When you publish. --- Views/_ViewImports.cshtml | 1 + 1 file changed, 1 insertion(+) diff --git a/Views/_ViewImports.cshtml b/Views/_ViewImports.cshtml index 8629c125..1872fd6b 100644 --- a/Views/_ViewImports.cshtml +++ b/Views/_ViewImports.cshtml @@ -1,2 +1,3 @@ @using AspCoreServer @addTagHelper "*, Microsoft.AspNetCore.SpaServices" +@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" From a019c9b9f01c22e66e1b75538bb6f39991ad698c Mon Sep 17 00:00:00 2001 From: Bill <OceansideBill@users.noreply.github.com> Date: Sat, 19 May 2018 09:56:10 -0700 Subject: [PATCH 13/60] feat(rxjs6): added modifications to support updates to ngx-bootstrap and get translation functioning with RXJS 6. --- ClientApp/app/app.component.ts | 20 +++++---- ClientApp/app/app.module.ts | 4 +- ClientApp/app/shared/user.service.ts | 4 +- ClientApp/polyfills/polyfills.ts | 9 +--- ClientApp/polyfills/rx-imports.ts | 30 ------------- package.json | 67 ++++++++++++++-------------- 6 files changed, 50 insertions(+), 84 deletions(-) delete mode 100644 ClientApp/polyfills/rx-imports.ts diff --git a/ClientApp/app/app.component.ts b/ClientApp/app/app.component.ts index d8b28150..614cba3e 100644 --- a/ClientApp/app/app.component.ts +++ b/ClientApp/app/app.component.ts @@ -1,7 +1,9 @@ -import { Component, OnInit, OnDestroy, Inject, ViewEncapsulation, RendererFactory2, PLATFORM_ID, Injector } from '@angular/core'; + +import {mergeMap, map, filter} from 'rxjs/operators'; +import { Component, OnInit, OnDestroy, Inject, ViewEncapsulation, RendererFactory2, PLATFORM_ID, Injector } from '@angular/core'; import { Router, NavigationEnd, ActivatedRoute, PRIMARY_OUTLET } from '@angular/router'; import { Meta, Title, DOCUMENT, MetaDefinition } from '@angular/platform-browser'; -import { Subscription } from 'rxjs/Subscription'; +import { Subscription } from 'rxjs'; import { isPlatformServer } from '@angular/common'; import { LinkService } from './shared/link.service'; @@ -60,15 +62,15 @@ export class AppComponent implements OnInit, OnDestroy { private _changeTitleOnNavigation() { - this.routerSub$ = this.router.events - .filter(event => event instanceof NavigationEnd) - .map(() => this.activatedRoute) - .map(route => { + this.routerSub$ = this.router.events.pipe( + filter(event => event instanceof NavigationEnd), + map(() => this.activatedRoute), + map(route => { while (route.firstChild) route = route.firstChild; return route; - }) - .filter(route => route.outlet === 'primary') - .mergeMap(route => route.data) + }), + filter(route => route.outlet === 'primary'), + mergeMap(route => route.data),) .subscribe((event) => { this._setMetaAndLinks(event); }); diff --git a/ClientApp/app/app.module.ts b/ClientApp/app/app.module.ts index 5394d67d..c0fa0985 100644 --- a/ClientApp/app/app.module.ts +++ b/ClientApp/app/app.module.ts @@ -7,7 +7,7 @@ import { FormsModule } from '@angular/forms'; import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser'; import { TransferHttpCacheModule } from '@nguniversal/common'; -import { Ng2BootstrapModule } from 'ngx-bootstrap'; +import { AccordionModule } from 'ngx-bootstrap'; // i18n support import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; @@ -57,7 +57,7 @@ export function createTranslateLoader(http: HttpClient, baseHref) { FormsModule, - Ng2BootstrapModule.forRoot(), // You could also split this up if you don't want the Entire Module imported + AccordionModule.forRoot(), // You could also split this up if you don't want the Entire Module imported // i18n support TranslateModule.forRoot({ diff --git a/ClientApp/app/shared/user.service.ts b/ClientApp/app/shared/user.service.ts index 6a3d7cb7..31110e7e 100644 --- a/ClientApp/app/shared/user.service.ts +++ b/ClientApp/app/shared/user.service.ts @@ -1,10 +1,10 @@ -import { Injectable, Inject, Injector } from '@angular/core'; +import { Injectable, Inject, Injector } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Http, URLSearchParams } from '@angular/http'; import { APP_BASE_HREF } from '@angular/common'; import { ORIGIN_URL } from '@nguniversal/aspnetcore-engine/tokens'; import { IUser } from '../models/User'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; @Injectable() diff --git a/ClientApp/polyfills/polyfills.ts b/ClientApp/polyfills/polyfills.ts index 4382a0e1..fd43e294 100644 --- a/ClientApp/polyfills/polyfills.ts +++ b/ClientApp/polyfills/polyfills.ts @@ -1,4 +1,4 @@ - + /*************************************************************************************************** * BROWSER POLYFILLS */ @@ -28,10 +28,3 @@ import 'core-js/es6/reflect'; import 'core-js/es7/reflect'; -/** - * Date, currency, decimal and percent pipes. - * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 - */ -// import 'intl'; // Run `npm install --save intl`. - -import './rx-imports'; diff --git a/ClientApp/polyfills/rx-imports.ts b/ClientApp/polyfills/rx-imports.ts deleted file mode 100644 index 506f102b..00000000 --- a/ClientApp/polyfills/rx-imports.ts +++ /dev/null @@ -1,30 +0,0 @@ - -/* -=- RxJs imports -=- - * - * Here you can place any RxJs imports so you don't have to constantly - * import them throughout your App :) - * - * This file is automatically imported into `polyfills.ts` (which is imported into browser/server modules) - */ - -// General Operators -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/do'; -import 'rxjs/add/operator/throttleTime'; -import 'rxjs/add/operator/distinctUntilChanged'; -import 'rxjs/add/operator/switchMap'; -import 'rxjs/add/operator/take'; -import 'rxjs/add/operator/debounceTime'; -import 'rxjs/add/operator/filter'; -import 'rxjs/add/operator/mergeMap'; -import 'rxjs/add/operator/concat'; -import 'rxjs/add/operator/catch'; -import 'rxjs/add/operator/first'; - -// Observable operators -import 'rxjs/add/observable/fromEvent'; -import 'rxjs/add/observable/interval'; -import 'rxjs/add/observable/fromPromise'; -import 'rxjs/add/observable/of'; -import 'rxjs/add/observable/concat'; - diff --git a/package.json b/package.json index 86dacd20..848a12a2 100644 --- a/package.json +++ b/package.json @@ -28,65 +28,66 @@ "@angular/router": "^5.0.0", "@nguniversal/aspnetcore-engine": "^5.0.0-beta.8", "@nguniversal/common": "^5.0.0-beta.8", - "@ngx-translate/core": "^8.0.0", - "@ngx-translate/http-loader": "^2.0.0", + "@ngx-translate/core": "^9.1.1", + "@ngx-translate/http-loader": "^2.0.1", "@types/node": "^7.0.12", "angular2-router-loader": "^0.3.5", "angular2-template-loader": "^0.6.2", "aspnet-prerendering": "^3.0.1", - "aspnet-webpack": "^2.0.1", - "awesome-typescript-loader": "^3.0.0", - "bootstrap": "^3.3.7", + "aspnet-webpack": "^2.0.3", + "awesome-typescript-loader": "^3.2.3", + "bootstrap": "^4.1.1", "bootstrap-sass": "^3.3.7", - "core-js": "^2.5.1", - "css": "^2.2.1", - "css-loader": "^0.28.7", - "event-source-polyfill": "^0.0.9", - "expose-loader": "^0.7.3", - "extract-text-webpack-plugin": "^3.0.0", - "file-loader": "^0.11.2", - "html-loader": "^0.5.1", + "core-js": "^2.5.6", + "css": "^2.2.3", + "css-loader": "^0.28.11", + "event-source-polyfill": "^0.0.12", + "expose-loader": "^0.7.5", + "extract-text-webpack-plugin": "^3.0.2", + "file-loader": "^1.1.11", + "html-loader": "^0.5.5", "isomorphic-fetch": "^2.2.1", - "jquery": "^2.2.1", - "json-loader": "^0.5.4", + "jquery": "^3.3.1", + "json-loader": "^0.5.7", "moment": "2.18.1", - "ngx-bootstrap": "2.0.0-beta.6", - "node-sass": "^4.5.2", + "ngx-bootstrap": "2.0.5", + "node-sass": "^4.9.0", "preboot": "^5.0.0", "raw-loader": "^0.5.1", "rimraf": "^2.6.2", - "rxjs": "^5.5.6", + "rxjs": "^6.1.0", + "rxjs-compat": "^6.1.0", "sass-loader": "^6.0.6", "style-loader": "^0.18.2", "to-string-loader": "^1.1.5", - "typescript": "~2.5.0", - "url-loader": "^0.5.7", + "typescript": "~2.7.2", + "url-loader": "^1.0.1", "webpack": "^3.6.0", - "webpack-hot-middleware": "^2.19.1", + "webpack-hot-middleware": "^2.22.1", "webpack-merge": "^4.1.0", - "zone.js": "^0.8.17" + "zone.js": "^0.8.26" }, "devDependencies": { "@angular/cli": "^1.7.0-beta.1", "@angular/compiler-cli": "^5.2.1", "@ngtools/webpack": "^1.9.0", - "@types/chai": "^3.4.34", - "@types/jasmine": "^2.5.37", - "chai": "^3.5.0", - "codelyzer": "^3.0.0", - "istanbul-instrumenter-loader": "^3.0.0", + "@types/chai": "^4.1.3", + "@types/jasmine": "^2.8.7", + "chai": "^4.1.2", + "codelyzer": "^3.1.2", + "istanbul-instrumenter-loader": "^3.0.1", "jasmine-core": "^2.5.2", "karma": "^1.7.1", "karma-chai": "^0.1.0", "karma-chrome-launcher": "^2.2.0", "karma-coverage": "^1.1.1", - "karma-jasmine": "^1.1.0", - "karma-mocha-reporter": "^2.2.4", + "karma-jasmine": "^1.1.2", + "karma-mocha-reporter": "^2.2.5", "karma-phantomjs-launcher": "^1.0.4", - "karma-remap-coverage": "^0.1.4", + "karma-remap-coverage": "^0.1.5", "karma-sourcemap-loader": "^0.3.7", - "karma-webpack": "^2.0.3", - "tslint": "^5.7.0", - "webpack-bundle-analyzer": "^2.9.0" + "karma-webpack": "^2.0.4", + "tslint": "^5.10.0", + "webpack-bundle-analyzer": "^2.12.0" } } From 0360ad5a8ef1830dd88d6c76aceae4a0f1879bf3 Mon Sep 17 00:00:00 2001 From: Robert Zeni <robert.zeni@mohawkcollege.ca> Date: Mon, 28 May 2018 13:40:45 -0400 Subject: [PATCH 14/60] feat(angular6): upgrade .net core 2.1, bootstrap 4, ngx-bootsrap 3, rxjs 6, etc closes #619 --- .vscode/launch.json | 6 +- Asp2017.csproj | 12 ++-- Asp2017.sln | 25 ++++++++ ClientApp/app/app.module.browser.ts | 4 +- ClientApp/app/app.module.server.ts | 4 +- .../app/containers/home/home.component.html | 2 +- .../app/containers/users/users.component.ts | 11 +++- README.md | 8 +-- package.json | 64 ++++++++++--------- webpack.config.js | 47 ++++++++++---- webpack.config.vendor.js | 40 ++++++++++-- 11 files changed, 152 insertions(+), 71 deletions(-) create mode 100644 Asp2017.sln diff --git a/.vscode/launch.json b/.vscode/launch.json index 08d4edf9..c26ab807 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -20,7 +20,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceRoot}/bin/Debug/netcoreapp2.0/Asp2017.dll", + "program": "${workspaceRoot}/bin/Debug/netcoreapp2.1/Asp2017.dll", "args": [], "cwd": "${workspaceRoot}", "stopAtEntry": false, @@ -51,7 +51,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceRoot}/bin/Debug/netcoreapp2.0/Asp2017.dll", + "program": "${workspaceRoot}/bin/Debug/netcoreapp2.1/Asp2017.dll", "args": [], "cwd": "${workspaceRoot}", "stopAtEntry": false, @@ -82,7 +82,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceRoot}/bin/Debug/netcoreapp2.0/Asp2017.dll", + "program": "${workspaceRoot}/bin/Debug/netcoreapp2.1/Asp2017.dll", "args": [], "cwd": "${workspaceRoot}", "stopAtEntry": false, diff --git a/Asp2017.csproj b/Asp2017.csproj index efb619c0..fe12d77e 100644 --- a/Asp2017.csproj +++ b/Asp2017.csproj @@ -1,17 +1,17 @@ <Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> - <TargetFramework>netcoreapp2.0</TargetFramework> + <TargetFramework>netcoreapp2.1</TargetFramework> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion> <IsPackable>false</IsPackable> </PropertyGroup> <ItemGroup> <!-- New Meta Package has SpaServices in It --> - <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" /> - <PackageReference Include="NETStandard.Library" Version="2.0.0" /> - <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.0" /> - <PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" /> + <PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.0-rc1-final" /> + <PackageReference Include="NETStandard.Library" Version="2.0.3" /> + <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.0-rc1-final" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0-rc1-final" /> + <PackageReference Include="Swashbuckle.AspNetCore" Version="2.4.0" /> </ItemGroup> <ItemGroup> <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" /> diff --git a/Asp2017.sln b/Asp2017.sln new file mode 100644 index 00000000..56a99822 --- /dev/null +++ b/Asp2017.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2018 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Asp2017", "Asp2017.csproj", "{BC28E9F7-E6EC-447D-AABD-17683BEAD625}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BC28E9F7-E6EC-447D-AABD-17683BEAD625}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC28E9F7-E6EC-447D-AABD-17683BEAD625}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC28E9F7-E6EC-447D-AABD-17683BEAD625}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC28E9F7-E6EC-447D-AABD-17683BEAD625}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DE341460-9041-458F-99D5-43FC7572CFA6} + EndGlobalSection +EndGlobal diff --git a/ClientApp/app/app.module.browser.ts b/ClientApp/app/app.module.browser.ts index fa983b67..f7371e80 100644 --- a/ClientApp/app/app.module.browser.ts +++ b/ClientApp/app/app.module.browser.ts @@ -7,7 +7,7 @@ import { ORIGIN_URL, REQUEST } from '@nguniversal/aspnetcore-engine/tokens'; import { AppModuleShared } from './app.module'; import { AppComponent } from './app.component'; import { BrowserTransferStateModule } from '@angular/platform-browser'; -import { BrowserPrebootModule } from 'preboot/browser'; +import { PrebootModule } from 'preboot'; export function getOriginUrl() { return window.location.origin; @@ -21,7 +21,7 @@ export function getRequest() { @NgModule({ bootstrap: [AppComponent], imports: [ - BrowserPrebootModule.replayEvents(), + PrebootModule.withConfig({ appRoot: 'app-root' }), BrowserAnimationsModule, // Our Common AppModule diff --git a/ClientApp/app/app.module.server.ts b/ClientApp/app/app.module.server.ts index 25711851..642a774c 100644 --- a/ClientApp/app/app.module.server.ts +++ b/ClientApp/app/app.module.server.ts @@ -7,7 +7,7 @@ import { AppModuleShared } from './app.module'; import { AppComponent } from './app.component'; import { ServerTransferStateModule } from '@angular/platform-server'; -import { ServerPrebootModule } from 'preboot/server'; +import { PrebootModule } from 'preboot'; @NgModule({ bootstrap: [AppComponent], @@ -16,7 +16,7 @@ import { ServerPrebootModule } from 'preboot/server'; AppModuleShared, ServerModule, - ServerPrebootModule.recordEvents({ appRoot: 'app-root' }), + PrebootModule.withConfig({ appRoot: 'app-root' }), NoopAnimationsModule, // HttpTransferCacheModule still needs fixes for 5.0 diff --git a/ClientApp/app/containers/home/home.component.html b/ClientApp/app/containers/home/home.component.html index 3a4db63b..3c08c588 100644 --- a/ClientApp/app/containers/home/home.component.html +++ b/ClientApp/app/containers/home/home.component.html @@ -12,7 +12,7 @@ <h2>{{ 'HOME_FEATURE_LIST_TITLE' | translate }} </h2> <ul> <li>ASP.NET Core 2.0 :: ( Visual Studio 2017 )</li> <li> - Angular 5.* front-end UI framework + Angular 6.* front-end UI framework <ul> <li>Angular **platform-server** (aka: Universal) - server-side rendering for SEO, deep-linking, and incredible performance.</li> diff --git a/ClientApp/app/containers/users/users.component.ts b/ClientApp/app/containers/users/users.component.ts index 6bca6a73..f6a0aa2d 100644 --- a/ClientApp/app/containers/users/users.component.ts +++ b/ClientApp/app/containers/users/users.component.ts @@ -1,8 +1,13 @@ import { - Component, OnInit, - // animation imports - trigger, state, style, transition, animate, Inject + Component, OnInit, Inject } from '@angular/core'; +import { + trigger, + state, + style, + animate, + transition +} from '@angular/animations'; import { IUser } from '../../models/User'; import { UserService } from '../../shared/user.service'; diff --git a/README.md b/README.md index 8cffbc2f..d2b1db31 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# ASP.NET Core 2.0 & Angular 5(+) Advanced Starter - with Server-side prerendering (for Angular SEO)! +# ASP.NET Core 2.0 & Angular 6(+) Advanced Starter - with Server-side prerendering (for Angular SEO)! ## By [DevHelp.Online](http://www.DevHelp.Online) -> Updated to the latest Angular 5.x +> Updated to the latest Angular 6.x > Note ServerTransferModule still in the works - fix coming soon @@ -81,7 +81,7 @@ This utilizes all the latest standards, no gulp, no bower, no typings, no manual - Codelyzer (for Real-time static code analysis) - VSCode & Atom provide real-time analysis out of the box. -- **ASP.NET Core 2.0** +- **ASP.NET Core 2.1** - Integration with NodeJS to provide pre-rendering, as well as any other Node module asset you want to use. @@ -113,7 +113,7 @@ This utilizes all the latest standards, no gulp, no bower, no typings, no manual ### Visual Studio 2017 -Make sure you have .NET Core 2.0 installed and/or VS2017 15.3. +Make sure you have .NET Core 2.1 installed and/or VS2017 15.3. VS2017 will automatically install all the neccessary npm & .NET dependencies when you open the project. Simply push F5 to start debugging ! diff --git a/package.json b/package.json index 848a12a2..9765601e 100644 --- a/package.json +++ b/package.json @@ -16,26 +16,26 @@ "clean": "rimraf wwwroot/dist clientapp/dist" }, "dependencies": { - "@angular/animations": "^5.0.0", - "@angular/common": "^5.0.0", - "@angular/compiler": "^5.0.0", - "@angular/core": "^5.0.0", - "@angular/forms": "^5.0.0", - "@angular/http": "^5.0.0", - "@angular/platform-browser": "^5.0.0", - "@angular/platform-browser-dynamic": "^5.0.0", - "@angular/platform-server": "^5.0.0", - "@angular/router": "^5.0.0", - "@nguniversal/aspnetcore-engine": "^5.0.0-beta.8", - "@nguniversal/common": "^5.0.0-beta.8", - "@ngx-translate/core": "^9.1.1", - "@ngx-translate/http-loader": "^2.0.1", - "@types/node": "^7.0.12", + "@angular/animations": "^6.0.3", + "@angular/common": "^6.0.3", + "@angular/compiler": "^6.0.3", + "@angular/core": "^6.0.3", + "@angular/forms": "^6.0.3", + "@angular/http": "^6.0.3", + "@angular/platform-browser": "^6.0.3", + "@angular/platform-browser-dynamic": "^6.0.3", + "@angular/platform-server": "^6.0.3", + "@angular/router": "^6.0.3", + "@nguniversal/aspnetcore-engine": "^6.0.0", + "@nguniversal/common": "^6.0.0", + "@ngx-translate/core": "^10.0.2", + "@ngx-translate/http-loader": "^3.0.1", + "@types/node": "^10.1.2", "angular2-router-loader": "^0.3.5", "angular2-template-loader": "^0.6.2", "aspnet-prerendering": "^3.0.1", "aspnet-webpack": "^2.0.3", - "awesome-typescript-loader": "^3.2.3", + "awesome-typescript-loader": "^5.0.0", "bootstrap": "^4.1.1", "bootstrap-sass": "^3.3.7", "core-js": "^2.5.6", @@ -43,38 +43,38 @@ "css-loader": "^0.28.11", "event-source-polyfill": "^0.0.12", "expose-loader": "^0.7.5", - "extract-text-webpack-plugin": "^3.0.2", "file-loader": "^1.1.11", "html-loader": "^0.5.5", "isomorphic-fetch": "^2.2.1", "jquery": "^3.3.1", "json-loader": "^0.5.7", - "moment": "2.18.1", - "ngx-bootstrap": "2.0.5", + "moment": "2.22.1", + "ngx-bootstrap": "^3.0.0", "node-sass": "^4.9.0", - "preboot": "^5.0.0", + "preboot": "^6.0.0-beta.4", "raw-loader": "^0.5.1", "rimraf": "^2.6.2", - "rxjs": "^6.1.0", - "rxjs-compat": "^6.1.0", - "sass-loader": "^6.0.6", - "style-loader": "^0.18.2", + "rxjs": "^6.2.0", + "rxjs-compat": "^6.2.0", + "sass-loader": "^7.0.1", + "style-loader": "^0.21.0", "to-string-loader": "^1.1.5", "typescript": "~2.7.2", "url-loader": "^1.0.1", - "webpack": "^3.6.0", - "webpack-hot-middleware": "^2.22.1", - "webpack-merge": "^4.1.0", + "webpack": "^4.9.1", + "webpack-hot-middleware": "^2.22.2", + "webpack-merge": "^4.1.2", "zone.js": "^0.8.26" }, "devDependencies": { - "@angular/cli": "^1.7.0-beta.1", - "@angular/compiler-cli": "^5.2.1", - "@ngtools/webpack": "^1.9.0", + "@angular/cli": "^6.0.5", + "@angular/compiler-cli": "6.0.3", + "@ngtools/webpack": "^6.0.5", "@types/chai": "^4.1.3", "@types/jasmine": "^2.8.7", "chai": "^4.1.2", "codelyzer": "^3.1.2", + "extract-text-webpack-plugin": "^4.0.0-beta.0", "istanbul-instrumenter-loader": "^3.0.1", "jasmine-core": "^2.5.2", "karma": "^1.7.1", @@ -88,6 +88,8 @@ "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "^2.0.4", "tslint": "^5.10.0", - "webpack-bundle-analyzer": "^2.12.0" + "uglifyjs-webpack-plugin": "^1.2.5", + "webpack-bundle-analyzer": "^2.13.1", + "webpack-cli": "^2.1.4" } } diff --git a/webpack.config.js b/webpack.config.js index a54d3135..d55cb8de 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -21,6 +21,7 @@ module.exports = (env) => { // Configuration in common to both client-side and server-side bundles const isDevBuild = !(env && env.prod); const sharedConfig = { + mode: isDevBuild ? "development" : "production", stats: { modules: false }, context: __dirname, resolve: { extensions: [ '.js', '.ts' ] }, @@ -64,16 +65,26 @@ module.exports = (env) => { tsConfigPath: './tsconfig.json', entryModule: path.join(__dirname, 'ClientApp/app/app.module.browser#AppModule'), exclude: ['./**/*.server.ts'] - }), - new webpack.optimize.UglifyJsPlugin({ - output: { - ascii_only: true, - } - }), + }) ]), devtool: isDevBuild ? 'cheap-eval-source-map' : false, node: { fs: "empty" + }, + optimization: { + minimizer: [].concat(isDevBuild ? [] : [ + // we specify a custom UglifyJsPlugin here to get source maps in production + new UglifyJsPlugin({ + cache: true, + parallel: true, + uglifyOptions: { + compress: false, + ecma: 6, + mangle: true + }, + sourceMap: true + }) + ]) } }); @@ -105,13 +116,6 @@ module.exports = (env) => { {} ) ] : [ - new webpack.optimize.UglifyJsPlugin({ - mangle: false, - compress: false, - output: { - ascii_only: true, - } - }), // Plugins that apply in production builds only new AngularCompilerPlugin({ mainPath: path.join(__dirname, 'ClientApp/boot.server.PRODUCTION.ts'), @@ -126,7 +130,22 @@ module.exports = (env) => { }, target: 'node', // switch to "inline-source-map" if you want to debug the TS during SSR - devtool: isDevBuild ? 'cheap-eval-source-map' : false + devtool: isDevBuild ? 'cheap-eval-source-map' : false, + optimization: { + minimizer: [].concat(isDevBuild ? [] : [ + // we specify a custom UglifyJsPlugin here to get source maps in production + new UglifyJsPlugin({ + cache: true, + parallel: true, + uglifyOptions: { + compress: false, + ecma: 6, + mangle: true + }, + sourceMap: true + }) + ]) + } }); return [clientBundleConfig, serverBundleConfig]; diff --git a/webpack.config.vendor.js b/webpack.config.vendor.js index 6d35d3ba..9a63c583 100644 --- a/webpack.config.vendor.js +++ b/webpack.config.vendor.js @@ -27,10 +27,11 @@ const nonTreeShakableModules = [ const allModules = treeShakableModules.concat(nonTreeShakableModules); module.exports = (env) => { - console.log(`env = ${JSON.stringify(env)}`) + console.log(`env = ${JSON.stringify(env)}`) const extractCSS = new ExtractTextPlugin('vendor.css'); const isDevBuild = !(env && env.prod); const sharedConfig = { + mode: isDevBuild ? "development" : "production", stats: { modules: false }, resolve: { extensions: [ '.js' ] }, module: { @@ -70,8 +71,23 @@ module.exports = (env) => { name: '[name]_[hash]' }) ].concat(isDevBuild ? [] : [ - new webpack.optimize.UglifyJsPlugin() - ]) + + ]), + optimization: { + minimizer: [].concat(isDevBuild ? [] : [ + // we specify a custom UglifyJsPlugin here to get source maps in production + new UglifyJsPlugin({ + cache: true, + parallel: true, + uglifyOptions: { + compress: false, + ecma: 6, + mangle: true + }, + sourceMap: true + }) + ]) + } }); const serverBundleConfig = merge(sharedConfig, { @@ -91,8 +107,22 @@ module.exports = (env) => { name: '[name]_[hash]' }) ].concat(isDevBuild ? [] : [ - new webpack.optimize.UglifyJsPlugin() - ]) + ]), + optimization: { + minimizer: [].concat(isDevBuild ? [] : [ + // we specify a custom UglifyJsPlugin here to get source maps in production + new UglifyJsPlugin({ + cache: true, + parallel: true, + uglifyOptions: { + compress: false, + ecma: 6, + mangle: true + }, + sourceMap: true + }) + ]) + } }); return [clientBundleConfig, serverBundleConfig]; From eff67f28a986be7fb36b16d002649e6ea37622e2 Mon Sep 17 00:00:00 2001 From: Mark Pieszak <mpieszak84@gmail.com> Date: Thu, 31 May 2018 14:56:48 -0400 Subject: [PATCH 15/60] fix(uglify, dotnetcore): update to 2.1, fix uglify issue, prod build fixed closes #632 --- .angular-cli.json | 25 -------------- Asp2017.csproj | 8 ++--- ClientApp/tsconfig.spec.json | 1 + angular.json | 63 ++++++++++++++++++++++++++++++++++++ package.json | 4 ++- webpack.config.js | 1 + webpack.config.vendor.js | 2 ++ 7 files changed, 74 insertions(+), 30 deletions(-) delete mode 100644 .angular-cli.json create mode 100644 angular.json diff --git a/.angular-cli.json b/.angular-cli.json deleted file mode 100644 index 4e8d017d..00000000 --- a/.angular-cli.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "project": { - "name": "AspnetCore-Angular-Universal" - }, - "apps": [ - { - "root": "ClientApp" - } - ], - "defaults": { - "styleExt": "scss", - "component": { - "spec": false - } - }, - "lint":[ - { - "project": "ClientApp/tsconfig.app.json" - }, - { - "project": "ClientApp/tsconfig.spec.json" - } - ] -} diff --git a/Asp2017.csproj b/Asp2017.csproj index fe12d77e..c8975b88 100644 --- a/Asp2017.csproj +++ b/Asp2017.csproj @@ -7,15 +7,15 @@ </PropertyGroup> <ItemGroup> <!-- New Meta Package has SpaServices in It --> - <PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.0-rc1-final" /> + <PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.0" /> <PackageReference Include="NETStandard.Library" Version="2.0.3" /> - <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.0-rc1-final" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0-rc1-final" /> + <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.0" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="2.4.0" /> </ItemGroup> <ItemGroup> <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" /> - </ItemGroup> + </ItemGroup> <ItemGroup> <!-- Files not to show in IDE --> <None Remove="yarn.lock" /> diff --git a/ClientApp/tsconfig.spec.json b/ClientApp/tsconfig.spec.json index 584cb0a4..de4e2a75 100644 --- a/ClientApp/tsconfig.spec.json +++ b/ClientApp/tsconfig.spec.json @@ -11,6 +11,7 @@ ] }, "files": [ + "polyfills.ts" ], "include": [ "**/*.spec.ts", diff --git a/angular.json b/angular.json new file mode 100644 index 00000000..42179045 --- /dev/null +++ b/angular.json @@ -0,0 +1,63 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "AspnetCore-Angular-Universal": { + "root": "", + "sourceRoot": "src", + "projectType": "application", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/", + "index": "ClientApp/index.html", + "main": "ClientApp/main.ts", + "tsConfig": "ClientApp/tsconfig.app.json", + "assets": [], + "styles": [], + "scripts": [] + }, + "configurations": {} + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "AspnetCore-Angular-Universal:build" + }, + "configurations": {} + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "AspnetCore-Angular-Universal:build" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "ClientApp/tsconfig.app.json", + "ClientApp/tsconfig.spec.json" + ], + "exclude": [] + } + } + } + }, + "AspnetCore-Angular-Universal-e2e": { + "root": "", + "sourceRoot": "", + "projectType": "application" + } + }, + "defaultProject": "AspnetCore-Angular-Universal", + "schematics": { + "@schematics/angular:component": { + "spec": false, + "styleext": "scss" + }, + "@schematics/angular:directive": {} + } +} \ No newline at end of file diff --git a/package.json b/package.json index 9765601e..61767b74 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "angular4-aspnetcore-universal", "version": "1.0.0-rc3", "scripts": { + "clean:install": "npm run clean && rimraf ./node_modules ./bin ./obj ./package-lock.json && dotnet restore && npm i", "lint": "tslint -p tsconfig.json", "test": "npm run build:vendor && karma start ClientApp/test/karma.conf.js", "test:watch": "npm run test -- --auto-watch --no-single-run", @@ -90,6 +91,7 @@ "tslint": "^5.10.0", "uglifyjs-webpack-plugin": "^1.2.5", "webpack-bundle-analyzer": "^2.13.1", - "webpack-cli": "^2.1.4" + "webpack-cli": "^2.1.4", + "@angular-devkit/build-angular": "~0.6.6" } } diff --git a/webpack.config.js b/webpack.config.js index d55cb8de..029207ac 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -14,6 +14,7 @@ const merge = require('webpack-merge'); const AngularCompilerPlugin = require('@ngtools/webpack').AngularCompilerPlugin; const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin; const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; +const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const { sharedModuleRules } = require('./webpack.additions'); diff --git a/webpack.config.vendor.js b/webpack.config.vendor.js index 9a63c583..b335a1f9 100644 --- a/webpack.config.vendor.js +++ b/webpack.config.vendor.js @@ -2,6 +2,7 @@ const path = require('path'); const webpack = require('webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const merge = require('webpack-merge'); +const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const treeShakableModules = [ '@angular/animations', '@angular/common', @@ -24,6 +25,7 @@ const nonTreeShakableModules = [ 'event-source-polyfill', // 'jquery', ]; + const allModules = treeShakableModules.concat(nonTreeShakableModules); module.exports = (env) => { From 2e12109b0fe957154edaf2de6a15614b37597f24 Mon Sep 17 00:00:00 2001 From: Mark Pieszak <mpieszak84@gmail.com> Date: Thu, 31 May 2018 15:04:21 -0400 Subject: [PATCH 16/60] docs(readme): update verbiage --- README.md | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index d2b1db31..7006242d 100644 --- a/README.md +++ b/README.md @@ -4,24 +4,22 @@ > Updated to the latest Angular 6.x -> Note ServerTransferModule still in the works - fix coming soon - <p align="center"> - <img src="http://github.com/docs/architecture.png" alt="ASP.NET Core 2.0 Angular 5+ Starter" title="ASP.NET Core 2.0 Angular 5+ Starter"> + <img src="http://github.com/docs/architecture.png" alt="ASP.NET Core 2.0 Angular 6+ Starter" title="ASP.NET Core 2.0 Angular 6+ Starter"> </p> -### Harness the power of Angular 5+, ASP.NET Core 2.0, now with SEO ! +### Harness the power of Angular 6+, ASP.NET Core 2.0, now with SEO ! Angular SEO in action: <p align="center"> - <img src="http://github.com/docs/angular2-seo.png" alt="ASP.NET Core Angular5 SEO" title="ASP.NET Core Angular5 SEO"> + <img src="http://github.com/docs/angular2-seo.png" alt="ASP.NET Core Angular6 SEO" title="ASP.NET Core Angular6 SEO"> </p> ### What is this repo? Live Demo here: http://aspnetcore-angular2-universal.azurewebsites.net This repository is maintained by [Angular](https://github.com/angular/angular) and is meant to be an advanced starter -for both ASP.NET Core 2.0 using Angular 5.0+, not only for the client-side, but to be rendered on the server for instant +for both ASP.NET Core 2.0 using Angular 6.0+, not only for the client-side, but to be rendered on the server for instant application paints (Note: If you don't need SSR [read here](#faq) on how to disable it). This is meant to be a Feature-Rich Starter application containing all of the latest technologies, best build systems available, and include many real-world examples and libraries needed in todays Single Page Applications (SPAs). @@ -47,7 +45,7 @@ This utilizes all the latest standards, no gulp, no bower, no typings, no manual > These are just some of the features found in this starter! -- ASP.NET 2.0 - VS2017 15.3 support now! +- ASP.NET 2.1 - VS2017 support now! - Azure delpoyment straight from VS2017 - Built in docker support through VS2017 - RestAPI (WebAPI) integration @@ -55,7 +53,7 @@ This utilizes all the latest standards, no gulp, no bower, no typings, no manual - Swagger WebAPI documentation when running in development mode - SignalR Chat demo! (Thanks to [@hakonamatata](https://github.com/hakonamatata)) -- **Angular 5.0.0** : +- **Angular 6.0.0** : - (Minimal) Angular-CLI integration - This is to be used mainly for Generating Components/Services/etc. - Usage examples: @@ -69,7 +67,7 @@ This utilizes all the latest standards, no gulp, no bower, no typings, no manual - Can be easily replaced with bootstrap4 (3 is provided for browser support) - Bootstrap using SCSS / SASS for easy theming / styling! -- **Webpack build system (Webpack 2)** +- **Webpack build system (Webpack 4)** - HMR : Hot Module Reloading/Replacement - Production builds w/ AoT Compilation @@ -87,7 +85,7 @@ This utilizes all the latest standards, no gulp, no bower, no typings, no manual - **Azure** - Microsoft Application Insights setup (for MVC & Web API routing) - - Client-side Angular2 Application Insights integration + - Client-side Angular Application Insights integration - If you're using Azure simply install `npm i -S @markpieszak/ng-application-insights` as a dependencies. - Note: Make sure only the Browser makes these calls ([usage info here](https://github.com/MarkPieszak/angular-application-insights/blob/master/README.md#usage)) - More information here: - https://github.com/MarkPieszak/angular-application-insights @@ -97,9 +95,7 @@ This utilizes all the latest standards, no gulp, no bower, no typings, no manual instrumentationKey: 'Your-Application-Insights-instrumentationKey' }) ``` - - -> Looking for the older 2.x branch? Go [here](https://github.com/MarkPieszak/aspnetcore-angular2-universal/tree/old-2.x-universal-branch) + ---- @@ -107,8 +103,8 @@ This utilizes all the latest standards, no gulp, no bower, no typings, no manual # Getting Started? -- **Make sure you have at least Node 6.x or higher (w/ npm 3+) installed!** -- **This repository uses ASP.Net Core 2.0, which has a hard requirement on .NET Core Runtime 2.0.0 and .NET Core SDK 2.0.0. Please install these items from [here](https://github.com/dotnet/core/blob/master/release-notes/download-archives/2.0.0-download.md)** +- **Make sure you have at least Node 8.11.1 or higher (w/ npm 5+) installed!** +- **This repository uses ASP.Net Core 2.1, which has a hard requirement on .NET Core Runtime 2.1 and .NET Core SDK 2.1. Please install these items from [here](https://blogs.msdn.microsoft.com/dotnet/2018/05/30/announcing-net-core-2-1/?WT.mc_id=blog-twitter-timheuer)** ### Visual Studio 2017 @@ -143,8 +139,9 @@ export ASPNETCORE_ENVIRONMENT=Development # Upcoming Features: -- Fix HttpTransferCacheModule & ServerTransferModule to work with aspnet-engine -- ~~Update to use npm [ngAspnetCoreEngine](https://github.com/angular/universal/pull/682) (still need to tweak a few things there)~~ +- Clean API / structure / simplify application +- Refactor to latest RxJs pipeable syntax +- Attempt to integrate with Angular-CLI fully ---- @@ -296,7 +293,7 @@ Take a look at the `_Layout.cshtml` file for example, notice how we let .NET han <head> <base href="http://github.com/" /> <!-- Title will be the one you set in your Angular application --> - <title>@ViewData["Title"] - AspNET.Core Angular 5.0.0 (+) starter + @ViewData["Title"] - AspNET.Core Angular 6.0.0 (+) starter @@ -335,9 +332,9 @@ Well now, your Client-side Angular will take over, and you'll have a fully funct # "Gotchas" -- This repository uses ASP.Net Core 2.0, which has a hard requirement on .NET Core Runtime 2.0.0 and .NET Core SDK 2.0.0. Please install these items from [here](https://github.com/dotnet/core/blob/master/release-notes/download-archives/2.0.0-download.md) +- This repository uses ASP.Net Core 2.1, which has a hard requirement on .NET Core Runtime 2.1 and .NET Core SDK 2.1. Please install these items from [here](https://blogs.msdn.microsoft.com/dotnet/2018/05/30/announcing-net-core-2-1/?WT.mc_id=blog-twitter-timheuer) -> When building components in Angular 5 there are a few things to keep in mind. +> When building components in Angular 6 there are a few things to keep in mind. - Make sure you provide Absolute URLs when calling any APIs. (The server can't understand relative paths, so `/api/whatever` will fail). From 8f6356ca35d1164b3c25ce112253e26da5511e39 Mon Sep 17 00:00:00 2001 From: Mark Pieszak Date: Thu, 31 May 2018 15:04:55 -0400 Subject: [PATCH 17/60] docs(readme): update --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7006242d..bdf713cc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ASP.NET Core 2.0 & Angular 6(+) Advanced Starter - with Server-side prerendering (for Angular SEO)! +# ASP.NET Core 2.1 & Angular 6(+) Advanced Starter - with Server-side prerendering (for Angular SEO)! ## By [DevHelp.Online](http://www.DevHelp.Online) @@ -19,7 +19,7 @@ Angular SEO in action: ### What is this repo? Live Demo here: http://aspnetcore-angular2-universal.azurewebsites.net This repository is maintained by [Angular](https://github.com/angular/angular) and is meant to be an advanced starter -for both ASP.NET Core 2.0 using Angular 6.0+, not only for the client-side, but to be rendered on the server for instant +for both ASP.NET Core 2.1 using Angular 6.0+, not only for the client-side, but to be rendered on the server for instant application paints (Note: If you don't need SSR [read here](#faq) on how to disable it). This is meant to be a Feature-Rich Starter application containing all of the latest technologies, best build systems available, and include many real-world examples and libraries needed in todays Single Page Applications (SPAs). From e1b8178d2845e54c584528cd1ea4e235777cd68f Mon Sep 17 00:00:00 2001 From: Mark Pieszak Date: Thu, 31 May 2018 15:39:02 -0400 Subject: [PATCH 18/60] chore(wording): update 5.0 references in text to 6.0 --- ClientApp/app/containers/home/home.component.html | 6 +++--- Startup.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ClientApp/app/containers/home/home.component.html b/ClientApp/app/containers/home/home.component.html index 3c08c588..7b6799b6 100644 --- a/ClientApp/app/containers/home/home.component.html +++ b/ClientApp/app/containers/home/home.component.html @@ -1,8 +1,8 @@ 

{{ title }}

- Enjoy the latest features from .NET Core & Angular 5.0! -
For more info check the repo here: AspNetCore-Angular2-Universal repo + Enjoy the latest features from .NET Core & Angular 6.x! +
For more info check the repo here: AspNetCore-Angular-Universal repo

@@ -10,7 +10,7 @@

{{ 'HOME_FEATURE_LIST_TITLE' | translate }}

    -
  • ASP.NET Core 2.0 :: ( Visual Studio 2017 )
  • +
  • ASP.NET Core 2.1 :: ( Visual Studio 2017 )
  • Angular 6.* front-end UI framework
      diff --git a/Startup.cs b/Startup.cs index db1d5d8d..4b07eaed 100644 --- a/Startup.cs +++ b/Startup.cs @@ -56,7 +56,7 @@ public void ConfigureServices(IServiceCollection services) // Register the Swagger generator, defining one or more Swagger documents services.AddSwaggerGen(c => { - c.SwaggerDoc("v1", new Info { Title = "Angular 5.0 Universal & ASP.NET Core advanced starter-kit web API", Version = "v1" }); + c.SwaggerDoc("v1", new Info { Title = "Angular 6.0 Universal & ASP.NET Core advanced starter-kit web API", Version = "v1" }); }); } From 352fab824aa428079f31a6902c1e95444660f7f8 Mon Sep 17 00:00:00 2001 From: GRIMMR3AP3R Date: Thu, 31 May 2018 14:46:41 -0500 Subject: [PATCH 19/60] feat(bootstrap4): add bootstrap4, docker support, fix bootstrap-sass issues * Real Bootstrap 4 * Real Boostrap 4 * Add docker support --- .dockerignore | 335 ++++++++++++++++++ ClientApp/app/_styles.scss | 45 +++ ClientApp/app/_variables.scss | 6 + ClientApp/app/app.component.html | 8 +- ClientApp/app/app.component.scss | 88 ++--- ClientApp/app/app.module.ts | 5 +- .../components/navmenu/navmenu.component.css | 106 ------ .../components/navmenu/navmenu.component.html | 89 +++-- .../components/navmenu/navmenu.component.scss | 114 ++++++ .../components/navmenu/navmenu.component.ts | 2 +- .../user-detail/user-detail.component.html | 25 +- .../user-detail/user-detail.component.scss | 4 + .../user-detail/user-detail.component.ts | 29 +- .../app/containers/home/home.component.html | 4 +- .../ngx-bootstrap.component.html | 2 +- .../app/containers/users/users.component.css | 64 ---- .../app/containers/users/users.component.html | 55 +-- .../app/containers/users/users.component.scss | 101 ++++++ .../app/containers/users/users.component.ts | 8 +- Dockerfile | 24 ++ Server/Data/DbInitializer.cs | 3 +- Views/Shared/_Layout.cshtml | 3 +- package.json | 1 - 23 files changed, 809 insertions(+), 312 deletions(-) create mode 100644 .dockerignore create mode 100644 ClientApp/app/_styles.scss create mode 100644 ClientApp/app/_variables.scss delete mode 100644 ClientApp/app/components/navmenu/navmenu.component.css create mode 100644 ClientApp/app/components/navmenu/navmenu.component.scss create mode 100644 ClientApp/app/components/user-detail/user-detail.component.scss delete mode 100644 ClientApp/app/containers/users/users.component.css create mode 100644 ClientApp/app/containers/users/users.component.scss create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..230e749b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,335 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +*/bin +*/obj +**/.toolstarget + + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ \ No newline at end of file diff --git a/ClientApp/app/_styles.scss b/ClientApp/app/_styles.scss new file mode 100644 index 00000000..42cf15fe --- /dev/null +++ b/ClientApp/app/_styles.scss @@ -0,0 +1,45 @@ +$body-bg: #f1f1f1; +$body-color: #111; +$theme-colors: ( "primary": #216DAD); +$theme-colors: ( "accent": #669ECD); + + +@import "http://github.com/~bootstrap/scss/bootstrap"; +.panel { + box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12); + height: 100%; + flex: 1; + background-color: rgba(255, 255, 255, .30); + border-radius: .25rem; + .title { + background-color: #86afd0; + color: #FFFFFF; + text-align: center; + border-top-left-radius: .25rem; + border-top-right-radius: .25rem; + } + .body { + display: flex; + } +} + +@include media-breakpoint-down(md) { + .panel { + .body { + flex-direction: column; + padding: 0px; + } + .title { + font-size: 1.5rem; + } + } +} + +@include media-breakpoint-up(md) { + .panel { + .body { + flex-direction: row; + padding: 1.5rem; + } + } +} diff --git a/ClientApp/app/_variables.scss b/ClientApp/app/_variables.scss new file mode 100644 index 00000000..2d34745a --- /dev/null +++ b/ClientApp/app/_variables.scss @@ -0,0 +1,6 @@ +@import "http://github.com/styles"; +$header-height:50px; +$menu-max-width:25%; +$navbar-default-bg: #312312; +$light-orange: #ff8c00; +$navbar-default-color: $light-orange; diff --git a/ClientApp/app/app.component.html b/ClientApp/app/app.component.html index 0345c682..45ca7f89 100644 --- a/ClientApp/app/app.component.html +++ b/ClientApp/app/app.component.html @@ -1,6 +1,6 @@ -
      - -
      -
      +
      + +
      +
      diff --git a/ClientApp/app/app.component.scss b/ClientApp/app/app.component.scss index 481063dc..8705be92 100644 --- a/ClientApp/app/app.component.scss +++ b/ClientApp/app/app.component.scss @@ -1,63 +1,71 @@ -$navbar-default-bg: #312312; -$light-orange: #ff8c00; -$navbar-default-color: $light-orange; +@import "http://github.com/variables"; +/* *** Overall APP Styling can go here *** + -------------------------------------------- + Note: This Component has ViewEncapsulation.None so the styles will bleed out -/* Import Bootstrap & Fonts */ -$icon-font-path: '~bootstrap-sass/assets/fonts/bootstrap/'; -@import "http://github.com/~bootstrap-sass/assets/stylesheets/bootstrap"; +*/ +body { + line-height: 18px; + padding-top: $header-height; +} +.body-content { + margin: auto; +} +h1 { + border-bottom: 3px theme-color("accent") solid; + font-size: 24px; +} -/* *** Overall APP Styling can go here *** - -------------------------------------------- - Note: This Component has ViewEncapsulation.None so the styles will bleed out +h2 { + font-size: 20px; +} -*/ -@media (max-width: 767px) { - body { - background: #f1f1f1; - line-height: 18px; - padding-top: 30px; - } +h1, +h2, +h3 { + padding: 3px 0; +} - h1 { - border-bottom: 3px #4189C7 solid; - font-size: 24px; - } +ul { + padding: 10px 25px; +} - h2 { - font-size: 20px; - } +ul li { + padding: 5px 0; +} - h1, h2, h3 { - padding: 3px 0; - } +blockquote { + margin: 25px 10px; + padding: 10px 35px 10px 10px; + border-left: 10px color("green") solid; + background: $gray-100; +} + +blockquote a, +blockquote a:hover { + color: $green; } -@media (min-width: 768px) { +@include media-breakpoint-up(lg) { body { - background: #f1f1f1; - line-height: 18px; - padding-top: 0px; + padding-top: 30px; + } + .body-content { + margin-left: $menu-max-width; } - h1 { border-bottom: 5px #4189C7 solid; font-size: 36px; } - h2 { font-size: 30px; } - - h1, h2, h3 { + h1, + h2, + h3 { padding: 10px 0; } } - -ul { padding: 10px 25px; } -ul li { padding: 5px 0; } - -blockquote { margin: 25px 10px; padding: 10px 35px 10px 10px; border-left: 10px #158a15 solid; background: #edffed; } -blockquote a, blockquote a:hover { color: #068006; } diff --git a/ClientApp/app/app.module.ts b/ClientApp/app/app.module.ts index c0fa0985..7cc9c4c0 100644 --- a/ClientApp/app/app.module.ts +++ b/ClientApp/app/app.module.ts @@ -3,7 +3,7 @@ import { RouterModule, PreloadAllModules } from '@angular/router'; import { CommonModule, APP_BASE_HREF } from '@angular/common'; import { HttpModule, Http } from '@angular/http'; import { HttpClientModule, HttpClient } from '@angular/common/http'; -import { FormsModule } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser'; import { TransferHttpCacheModule } from '@nguniversal/common'; @@ -54,9 +54,8 @@ export function createTranslateLoader(http: HttpClient, baseHref) { HttpClientModule, TransferHttpCacheModule, BrowserTransferStateModule, - - FormsModule, + ReactiveFormsModule, AccordionModule.forRoot(), // You could also split this up if you don't want the Entire Module imported // i18n support diff --git a/ClientApp/app/components/navmenu/navmenu.component.css b/ClientApp/app/components/navmenu/navmenu.component.css deleted file mode 100644 index 8d86aa03..00000000 --- a/ClientApp/app/components/navmenu/navmenu.component.css +++ /dev/null @@ -1,106 +0,0 @@ -li .glyphicon { - margin-right: 10px; -} - -/* Highlighting rules for nav menu items */ -li.link-active a, -li.link-active a:hover, -li.link-active a:focus { - background-color: #4189C7; - color: white; -} - -/* Keep the nav menu independent of scrolling and on top of other items */ -.main-nav { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 1; -} - -.icon-bar { - background-color: #4189C7; -} - - -@media (max-width: 767px) { - /* Apply for small displays */ - .main-nav { - width: 100%; - } - - .navbar-brand { - font-size: 14px; - background-color: #f1f1f1; - } - .navbar-toggle { - padding: 0px 5px; - margin-top: 0px; - height: 26px; - } - - .navbar-link { - margin-top: 4px; - margin-left: 45px; - position: fixed; - } - - .navbar-collapse { - background-color: white; - } - - .navbar a { - /* If a menu item's text is too long, truncate it */ - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - padding-right: 5px; - } -} - -@media (min-width: 768px) { - /* On small screens, convert the nav menu to a vertical sidebar */ - .main-nav { - height: 100%; - max-width: 330px; - width: calc(25% - 20px); - } - .navbar { - border-radius: 0px; - border-width: 0px; - height: 100%; - } - .navbar-brand{ - width: 100%; - } - .navbar-link { - display: block; - width: 100% - } - .navbar-header { - float: none; - } - .navbar-collapse { - border-top: 1px solid #444; - padding: 0px; - } - .navbar ul { - float: none; - } - .navbar li { - float: none; - font-size: 15px; - margin: 6px; - } - .navbar li a { - padding: 10px 16px; - border-radius: 4px; - } - .navbar a { - /* If a menu item's text is too long, truncate it */ - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } -} diff --git a/ClientApp/app/components/navmenu/navmenu.component.html b/ClientApp/app/components/navmenu/navmenu.component.html index 08e1b0fc..36c187ed 100644 --- a/ClientApp/app/components/navmenu/navmenu.component.html +++ b/ClientApp/app/components/navmenu/navmenu.component.html @@ -1,50 +1,41 @@ -
    -

    DevHelp.Online

    +

    Trilon Consulting - Trilon.io

    Consulting | Development | Training | Workshops

    - Get your Team or Application up to speed by working with some of the leading industry experts in JavaScript & ASP.NET!
    + Get your Team or Application up to speed by working with some of the leading industry experts in JavaScript, Node / NestJS, & ASP.NET!

    Follow us on Twitter!

    - @DevHelpOnline | + @trilon_io | @MarkPieszak

    From 83b0301b1db1ed34e1d16d8ef9fa4750c8b970d8 Mon Sep 17 00:00:00 2001 From: Mark Pieszak Date: Sun, 17 Mar 2019 11:55:39 -0400 Subject: [PATCH 53/60] docs: update --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e6ed1417..b0b14e9f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,15 @@ # ASP.NET Core 2.1 & Angular 7(+) Advanced Starter - PWA & Server-side prerendering (for Angular SEO)! -## By [Trilon.io](https://Trilon.io) +## Made with :heart: by [Trilon.io](https://Trilon.io) +

    + + Trilon.io - Angular Universal, NestJS, JavaScript Application Consulting Development and Training + +

    + +--- -> Updated to the latest Angular 7.x +## High-level architectural diagram

    ASP.NET Core 2.1 Angular 7+ Starter @@ -471,7 +478,9 @@ Check out **[Trilon.io](https://Trilon.io)** for more info! Twitter [@Trilon_io] Contact us at , and let's talk about your projects needs.

    - Trilon.io - Angular Universal, NestJS, JavaScript Application Consulting Development and Training + + Trilon.io - Angular Universal, NestJS, JavaScript Application Consulting Development and Training +

    ## Follow Trilon online: From 7e2468a7d01472acf15f430b50ca596a64a62d6b Mon Sep 17 00:00:00 2001 From: Mark Pieszak Date: Sun, 17 Mar 2019 11:57:52 -0400 Subject: [PATCH 54/60] docs: update --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index b0b14e9f..b6402f4b 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,12 @@ # ASP.NET Core 2.1 & Angular 7(+) Advanced Starter - PWA & Server-side prerendering (for Angular SEO)! -## Made with :heart: by [Trilon.io](https://Trilon.io) +### Made with :heart: by [Trilon.io](https://Trilon.io)

    Trilon.io - Angular Universal, NestJS, JavaScript Application Consulting Development and Training

    ---- - -## High-level architectural diagram

    ASP.NET Core 2.1 Angular 7+ Starter From 4aa16404f1b1a0dd1b4e70b7dd891b0006eabd9b Mon Sep 17 00:00:00 2001 From: Mark Pieszak Date: Mon, 18 Mar 2019 12:32:39 -0400 Subject: [PATCH 55/60] fix: rxjs pin to 6.2.2 closes #714 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 99ba5443..01bc52bc 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "preboot": "^7.0.0", "raw-loader": "^1.0.0", "rimraf": "^2.6.3", - "rxjs": "~6.4.0", + "rxjs": "6.2.2", "sass-loader": "^7.1.0", "style-loader": "^0.23.1", "to-string-loader": "^1.1.5", From 9cf8b7ba6ee15c01e2ec02a77421e7ee77eb6254 Mon Sep 17 00:00:00 2001 From: Mark Pieszak Date: Fri, 5 Apr 2019 12:17:42 -0400 Subject: [PATCH 56/60] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b6402f4b..8e933ca5 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ### Made with :heart: by [Trilon.io](https://Trilon.io)

    - Trilon.io - Angular Universal, NestJS, JavaScript Application Consulting Development and Training + Trilon.io - Angular Universal, NestJS, JavaScript Application Consulting Development and Training

    @@ -476,7 +476,7 @@ Contact us at , and let's talk about your projects needs.

    - Trilon.io - Angular Universal, NestJS, JavaScript Application Consulting Development and Training + Trilon.io - Angular Universal, NestJS, JavaScript Application Consulting Development and Training

    From 82dc1897c03f9546b613b72e4a72040b85ab54f1 Mon Sep 17 00:00:00 2001 From: Mark Pieszak Date: Fri, 5 Apr 2019 12:45:04 -0400 Subject: [PATCH 57/60] Update README.md --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8e933ca5..ecac2050 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,6 @@

    - -

    - ASP.NET Core 2.1 Angular 7+ Starter -

    - ### Harness the power of Angular 7+, ASP.NET Core 2.1, now with SEO ! Angular SEO in action: @@ -20,6 +15,12 @@ Angular SEO in action: ASP.NET Core Angular7 SEO

    +### Angular Universal Application Architecture + +

    + ASP.NET Core 2.1 Angular 7+ Starter +

    + ### What is this repo? Live Demo here: http://aspnetcore-angular2-universal.azurewebsites.net This repository is maintained by [Trilon.io](https://Trilon.io) and the [Angular](https://github.com/angular/angular) Universal team and is meant to be an advanced starter From b8ec2b0ff276a187e3821797fbd03201842d5311 Mon Sep 17 00:00:00 2001 From: Mark Pieszak Date: Fri, 5 Apr 2019 12:45:28 -0400 Subject: [PATCH 58/60] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ecac2050..482cc672 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@

    +--- + ### Harness the power of Angular 7+, ASP.NET Core 2.1, now with SEO ! Angular SEO in action: From 55f0105f807946cff515aaa0ef535de1329c2b9f Mon Sep 17 00:00:00 2001 From: Mark Pieszak Date: Sun, 7 Apr 2019 17:56:32 -0400 Subject: [PATCH 59/60] Update README.md --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 482cc672..f52bdf4d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # ASP.NET Core 2.1 & Angular 7(+) Advanced Starter - PWA & Server-side prerendering (for Angular SEO)! -### Made with :heart: by [Trilon.io](https://Trilon.io) -

    +--- + +
    +

    - Trilon.io - Angular Universal, NestJS, JavaScript Application Consulting Development and Training + Trilon.io - Angular Universal, NestJS, JavaScript Application Consulting Development and Training

    + +

    Made with :heart: by Trilon.io

    + --- ### Harness the power of Angular 7+, ASP.NET Core 2.1, now with SEO ! From 837b134516631afb3034d40278e2bc9f170acdba Mon Sep 17 00:00:00 2001 From: Detys Date: Mon, 27 May 2019 02:49:07 +0300 Subject: [PATCH 60/60] fix(seeding): seeding now follows .NET Core 2.0 best practices --- Program.cs | 60 ++++++++++++------- Server/Data/CoreEFStartup.cs | 17 ++++++ Server/Data/LoggingEFStartup.cs | 13 ++++ ...itializer.cs => SimpleContentEFStartup.cs} | 24 ++++---- 4 files changed, 80 insertions(+), 34 deletions(-) create mode 100644 Server/Data/CoreEFStartup.cs create mode 100644 Server/Data/LoggingEFStartup.cs rename Server/Data/{DbInitializer.cs => SimpleContentEFStartup.cs} (67%) diff --git a/Program.cs b/Program.cs index 69e56419..fdf46454 100644 --- a/Program.cs +++ b/Program.cs @@ -6,28 +6,42 @@ using Microsoft.Extensions.Logging; using System; using System.IO; +using System.Threading.Tasks; -public class Program { - public static void Main (string[] args) { - var host = BuildWebHost (args); - using (var scope = host.Services.CreateScope ()) { - var services = scope.ServiceProvider; - try { - var context = services.GetRequiredService(); - DbInitializer.Initialize(context); - } catch (Exception ex) { - var logger = services.GetRequiredService> (); - logger.LogError (ex, "An error occurred while seeding the database."); - } - } +public class Program +{ + public static async Task Main(string[] args) + { + var host = BuildWebHost(args); + using (var scope = host.Services.CreateScope()) + { + var services = scope.ServiceProvider; - host.Run (); - } - public static IWebHost BuildWebHost (string[] args) => - WebHost.CreateDefaultBuilder (args) - .UseKestrel () - .UseContentRoot (Directory.GetCurrentDirectory ()) - .UseIISIntegration () - .UseStartup () - .Build (); - } + try + { + await EnsureDataStorageIsReady(services); + + } catch (Exception ex) + { + var logger = services.GetRequiredService>(); + logger.LogError(ex, "An error occurred while seeding the database."); + } + } + + host.Run(); + } + public static IWebHost BuildWebHost(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .Build(); + + private static async Task EnsureDataStorageIsReady(IServiceProvider services) + { + await CoreEFStartup.InitializeDatabaseAsync(services); + await SimpleContentEFStartup.InitializeDatabaseAsync(services); + await LoggingEFStartup.InitializeDatabaseAsync(services); + } +} diff --git a/Server/Data/CoreEFStartup.cs b/Server/Data/CoreEFStartup.cs new file mode 100644 index 00000000..d3947ef1 --- /dev/null +++ b/Server/Data/CoreEFStartup.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; + +namespace AspCoreServer.Data +{ + public static class CoreEFStartup + { + public static async Task InitializeDatabaseAsync(IServiceProvider services) + { + var context = services.GetRequiredService(); + + await context.Database.EnsureCreatedAsync(); + } + + } +} diff --git a/Server/Data/LoggingEFStartup.cs b/Server/Data/LoggingEFStartup.cs new file mode 100644 index 00000000..edf33498 --- /dev/null +++ b/Server/Data/LoggingEFStartup.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading.Tasks; + +namespace AspCoreServer.Data +{ + public static class LoggingEFStartup + { + public static async Task InitializeDatabaseAsync(IServiceProvider services) + { + //Implent to your hearts' content + } + } +} diff --git a/Server/Data/DbInitializer.cs b/Server/Data/SimpleContentEFStartup.cs similarity index 67% rename from Server/Data/DbInitializer.cs rename to Server/Data/SimpleContentEFStartup.cs index 919c9282..db160dee 100644 --- a/Server/Data/DbInitializer.cs +++ b/Server/Data/SimpleContentEFStartup.cs @@ -1,16 +1,20 @@ using System; -using System.Linq; -using AspCoreServer; +using System.Threading.Tasks; using AspCoreServer.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -namespace AspCoreServer.Data { - public static class DbInitializer { - public static void Initialize (SpaDbContext context) { - context.Database.EnsureCreated (); +namespace AspCoreServer.Data +{ + public static class SimpleContentEFStartup + { + public static async Task InitializeDatabaseAsync(IServiceProvider services) + { + var context = services.GetRequiredService(); - if (context.User.Any ()) { + + if (await context.User.AnyAsync()) + { return; // DB has been seeded } var users = new User[] { @@ -27,11 +31,9 @@ public static void Initialize (SpaDbContext context) { new User () { Name = "Gaulomatic" }, new User () { Name = "GRIMMR3AP3R" } }; + await context.User.AddRangeAsync(users); - foreach (var s in users) { - context.User.Add (s); - } - context.SaveChanges (); + await context.SaveChangesAsync(); } } }