詳解在ASP.NET Core中使用Angular2以及與Angular2的Token base身份認證
Angular2是對Angular1的一次徹底的,破壞性的更新。
相對于Angular1.x,借用某果的廣告語,唯一的不同,就是處處都不同。
•首先,推薦的語言已經(jīng)不再是Javascript,取而代之的TypeScript,(TypeScript = ES6 + 類型系統(tǒng) + 類型注解), TypeScriipt的類型系統(tǒng)對于開發(fā)復雜的單頁Web app大有幫助,同時編譯成javascript后的執(zhí)行效率也比大多數(shù)手寫javascript要快。有興趣的同學可以查閱官方文檔:英文傳送門 |中文傳送門。
•得益于徹底重構(gòu),性能相對于Angular1.x有了大幅提升,也更適合再全平臺部署。
•Angular2是基于Component的,Component可以理解為是1.x時代的Controller + $Scope + view
•View的很多語法也做了更新,比如<li ng-repeat="movie in vm.movies"></li> 變成了 <li *ngFor="let movie of movies"></li>
關(guān)于Angular2,強烈建議查閱官方文檔:英文傳送門| 中文傳送門
注意:本文章屬于Step by step + Code Sample教程,且篇幅較長,建議下載本Sample并跟著本文進度自己重做一遍本例,下載完整代碼并分析代碼結(jié)構(gòu)才有意義,下載地址:How to authorization Angular 2 app with asp.net core web api
1.前期準備
•推薦使用VS2015 Update3或更新的版本完成本示例,下載地址:http://www.dbjr.com.cn/softjc/446184.html
•你需要安裝.NET Core開發(fā)環(huán)境,這里提供VS版: http://www.dbjr.com.cn/softs/472362.html
•安裝Node.js 版本5.0.0或以上,(在本例中,這個主要是編譯TypeScript用的)下載地址:Node.js and NPM
•NPM 3.0.0或以上,默認NPM會隨著Node.js一并安裝完畢。(在本例中,這個主要是下載各種Angular的各個包用的,參考VS中的Nuget)
2.創(chuàng)建項目
在VS中新建項目,項目類型選擇 ASP.NET Core Web Application(.Net Core),輸入項目名稱為:CSAuthorAngular2InASPNetCore,Template選擇為Empty.
3.在項目中整合Angular2
3.1.配置Startup.cs
注:添加下面的代碼時IDE會報代碼錯誤,這是因為還沒有引用對用的包,進入報錯的這一行,點擊燈泡,加載對應的包就可以了。

(圖文無關(guān))
在ConfigureServices中添加如下代碼
services.AddMvc();
這里是添加MVC服務
在Configure中添加如下代碼
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}");
});
第一句是啟用靜態(tài)文件,第二句是應用MVC模式并添加路由配置。
完整的代碼應該是這個樣子
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}");
});
}
}
3.2.添加控制器以及視圖
3.2.1.在項目根目錄下添加Controllers目錄,并在其中添加一個控制器HomeController.cs,默認代碼即可。
3.2.2.在項目跟目錄下創(chuàng)建Views目錄,在Views目錄中新建目錄Home, 最后在Home目錄中新建視圖Index.cshtml,內(nèi)容應該是這樣:
<html>
<head>
<title>Angular QuickStart</title>
<base href="/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 1. Load libraries -->
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<!-- 2. Configure SystemJS -->
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
現(xiàn)在運行項目的話你僅僅能看到一個Loading,再控制臺中你還能看到錯誤,這是因為我們還沒有配置Angular。讓我們前往wwwroot目錄。
3.3.在項目的wwwroot目錄中添加如下結(jié)構(gòu):
3.3.1搭建Angular2基礎(chǔ)環(huán)境
•package.json
{
"name": "angular-quickstart",
"version": "1.0.0",
"scripts": {
"start": "tsc && concurrently \"tsc -w\" \"lite-server\" ",
"lite": "lite-server",
"postinstall": "typings install",
"tsc": "tsc",
"tsc:w": "tsc -w",
"typings": "typings"
},
"licenses": [
{
"type": "MIT",
"url": "https://github.com/angular/angular.io/blob/master/LICENSE"
}
],
"dependencies": {
"@angular/common": "2.0.2",
"@angular/compiler": "2.0.2",
"@angular/core": "2.0.2",
"@angular/forms": "2.0.2",
"@angular/http": "2.0.2",
"@angular/platform-browser": "2.0.2",
"@angular/platform-browser-dynamic": "2.0.2",
"@angular/router": "3.0.2",
"@angular/upgrade": "2.0.2",
"angular-in-memory-web-api": "0.1.5",
"bootstrap": "3.3.7",
"core-js": "2.4.1",
"reflect-metadata": "0.1.8",
"rxjs": "5.0.0-beta.12",
"systemjs": "0.19.39",
"zone.js": "0.6.25"
},
"devDependencies": {
"concurrently": "3.0.0",
"gulp": "^3.9.1",
"lite-server": "2.2.2",
"typescript": "2.0.3",
"typings": "1.4.0"
}
}
•systemjs.config.js
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'app',
// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.js',
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
}
}
});
})(this);
•tsconfig.js
{
"compileOnSave": true,
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
},
"exclude": [
"node_modules"
]
}
•typings.json(注,在最新文檔中typings已被npm的@types替代,參見官方文檔:文檔變更日志)
{
"globalDependencies": {
"core-js": "registry:dt/core-js#0.0.0+20160725163759",
"jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
"node": "registry:dt/node#6.0.0+20160909174046"
}
}
右擊wwwroot中的Package.json,選擇Restore Packages(或者在CMD下進入wwwroot目錄,并執(zhí)行命令 npm install),npm會去下載需要的包,并存儲于node_modules目錄中。

3.3.2.配置啟動文件以啟用Angular2
在wwwroot下新建目錄app,app擁有如下文件:
•app.component.ts
import { Component } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'my-app',
template: "this is in angular2",
})
export class AppComponent {
}
可以發(fā)現(xiàn)被@Component裝飾屬性裝飾了AppComponent,selector指代你Component的占位符,比如本例中你可以再Home/index.cshtml中發(fā)現(xiàn)一段這樣的標記
<my-app>Loading...</my-app>
template既為該Component的View,不要忘記moduleId,不添加它會出現(xiàn)很多奇怪的問題。
•app.module.ts
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { AppComponent } from "./app.component";
@NgModule({
bootstrap: [AppComponent],
imports: [
BrowserModule
],
declarations: [
AppComponent
]
})
export class AppModule { }
•main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);
基礎(chǔ)整合完畢。
按F5 Debug一下,現(xiàn)在你能再瀏覽器中看到一句話:this is in angular 2

4.實現(xiàn)身份認證
廢了半天勁,看著很傻,沒有任何成就感。怎么辦,讓我們再深入一點,接下來我們來為Angular2完成一個Token base的身份驗證,我會把Angular2的routing,data bind,service,http,等等你工作中最常用到的挨個演示一遍。
4.1.Server端
4.1.1.創(chuàng)建一些輔助類
4.1.1.1.在項目根目錄下創(chuàng)建一個文件夾Auth,并添加RSAKeyHelper.cs以及TokenAuthOption.cs兩個文件
•在RSAKeyHelper.cs中
using System.Security.Cryptography;
namespace CSTokenBaseAuth.Auth
{
public class RSAKeyHelper
{
public static RSAParameters GenerateKey()
{
using (var key = new RSACryptoServiceProvider(2048))
{
return key.ExportParameters(true);
}
}
}
}
•在TokenAuthOption.cs中
using System;
using Microsoft.IdentityModel.Tokens;
namespace CSTokenBaseAuth.Auth
{
public class TokenAuthOption
{
public static string Audience { get; } = "ExampleAudience";
public static string Issuer { get; } = "ExampleIssuer";
public static RsaSecurityKey Key { get; } = new RsaSecurityKey(RSAKeyHelper.GenerateKey());
public static SigningCredentials SigningCredentials { get; } = new SigningCredentials(Key, SecurityAlgorithms.RsaSha256Signature);
public static TimeSpan ExpiresSpan { get; } = TimeSpan.FromMinutes(20);
}
}
4.1.1.2.在項目根目錄下創(chuàng)建目錄Model,并在其中添加RequestResult.cs,代碼應該是這樣。
public class RequestResult
{
public RequestState State { get; set; }
public string Msg { get; set; }
public Object Data { get; set; }
}
public enum RequestState
{
Failed = -1,
NotAuth = 0,
Success = 1
}
4.1.2更新Startup.cs
在ConfigureServices中添加如下代碼:
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
這里是添加身份認證服務
在Configure方法中添加如下代碼:
app.UseExceptionHandler(appBuilder =>
{
appBuilder.Use(async (context, next) =>
{
var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature;
//when authorization has failed, should retrun a json message to client
if (error != null && error.Error is SecurityTokenExpiredException)
{
context.Response.StatusCode = 401;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new RequestResult
{
State = RequestState.NotAuth,
Msg = "token expired"
}));
}
//when orther error, retrun a error message json to client
else if (error != null && error.Error != null)
{
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new RequestResult
{
State = RequestState.Failed,
Msg = error.Error.Message
}));
}
//when no error, do next.
else await next();
});
});
本段是Handle當身份認證失敗時拋出的異常,并返回合適的json
在相同的方法中添加另外一段代碼:
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
TokenValidationParameters = new TokenValidationParameters()
{
IssuerSigningKey = TokenAuthOption.Key,
ValidAudience = TokenAuthOption.Audience,
ValidIssuer = TokenAuthOption.Issuer,
// When receiving a token, check that we've signed it.
ValidateIssuerSigningKey = true,
// When receiving a token, check that it is still valid.
ValidateLifetime = true,
// This defines the maximum allowable clock skew - i.e. provides a tolerance on the token expiry time
// when validating the lifetime. As we're creating the tokens locally and validating them on the same
// machines which should have synchronised time, this can be set to zero. Where external tokens are
// used, some leeway here could be useful.
ClockSkew = TimeSpan.FromMinutes(0)
}
});
本段代碼是應用JWTBearerAuthentication身份認證。
4.1.3.TokenAuthController.cs
在Controllers中新建一個Web API Controller Class,命名為TokenAuthController.cs。我們將在這里完成登錄授權(quán),
在同文件下添加兩個類,分別用來模擬用戶模型,以及用戶存儲,代碼應該是這樣:
public class User
{
public Guid ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
public static class UserStorage
{
public static List<User> Users { get; set; } = new List<User> {
new User {ID=Guid.NewGuid(),Username="user1",Password = "user1psd" },
new User {ID=Guid.NewGuid(),Username="user2",Password = "user2psd" },
new User {ID=Guid.NewGuid(),Username="user3",Password = "user3psd" }
};
}
接下來在TokenAuthController.cs中添加如下方法
private string GenerateToken(User user, DateTime expires)
{
var handler = new JwtSecurityTokenHandler();
ClaimsIdentity identity = new ClaimsIdentity(
new GenericIdentity(user.Username, "TokenAuth"),
new[] {
new Claim("ID", user.ID.ToString())
}
);
var securityToken = handler.CreateToken(new SecurityTokenDescriptor
{
Issuer = TokenAuthOption.Issuer,
Audience = TokenAuthOption.Audience,
SigningCredentials = TokenAuthOption.SigningCredentials,
Subject = identity,
Expires = expires
});
return handler.WriteToken(securityToken);
}
該方法僅僅只是生成一個Auth Token,接下來我們來添加另外一個方法來調(diào)用它
在相同文件中添加如下代碼
[HttpPost]
public string GetAuthToken(User user)
{
var existUser = UserStorage.Users.FirstOrDefault(u => u.Username == user.Username && u.Password == user.Password);
if (existUser != null)
{
var requestAt = DateTime.Now;
var expiresIn = requestAt + TokenAuthOption.ExpiresSpan;
var token = GenerateToken(existUser, expiresIn);
return JsonConvert.SerializeObject(new {
stateCode = 1,
requertAt = requestAt,
expiresIn = TokenAuthOption.ExpiresSpan.TotalSeconds,
accessToken = token
});
}
else
{
return JsonConvert.SerializeObject(new { stateCode = -1, errors = "Username or password is invalid" });
}
}
接下來我們來完成授權(quán)部分,在相同的文件中添加如下代碼:
public string GetUserInfo()
{
var claimsIdentity = User.Identity as ClaimsIdentity;
return JsonConvert.SerializeObject(new RequestResult
{
State = RequestState.Success,
Data = new
{
UserName = claimsIdentity.Name
}
});
}
為方法添加裝飾屬性
[HttpGet]
[Authorize("Bearer")]
第二行代碼說明這個action需要身份驗證。
該文件完整的代碼應該是這個樣子:
using System;
using System.Collections.Generic;
using System.Linq;using Microsoft.AspNetCore.Mvc;
using CSAuthorAngular2InASPNetCore.Auth;
using System.IdentityModel.Tokens.Jwt;
using Newtonsoft.Json;
using System.Security.Claims;
using System.Security.Principal;
using Microsoft.IdentityModel.Tokens;
using CSAuthorAngular2InASPNetCore.Model;
using Microsoft.AspNetCore.Authorization;
namespace CSAuthorAngular2InASPNetCore.Controllers
{
[Route("api/[controller]")]
public class TokenAuthController : Controller
{
[HttpPost]
public string GetAuthToken([FromBody]User user)
{
var existUser = UserStorage.Users.FirstOrDefault(u => u.Username == user.Username && u.Password == user.Password);
if (existUser != null)
{
var requestAt = DateTime.Now;
var expiresIn = requestAt + TokenAuthOption.ExpiresSpan;
var token = GenerateToken(existUser, expiresIn);
return JsonConvert.SerializeObject(new RequestResult
{
State = RequestState.Success,
Data = new
{
requertAt = requestAt,
expiresIn = TokenAuthOption.ExpiresSpan.TotalSeconds,
tokeyType = TokenAuthOption.TokenType,
accessToken = token
}
});
}
else
{
return JsonConvert.SerializeObject(new RequestResult
{
State = RequestState.Failed,
Msg = "Username or password is invalid"
});
}
}
private string GenerateToken(User user, DateTime expires)
{
var handler = new JwtSecurityTokenHandler();
ClaimsIdentity identity = new ClaimsIdentity(
new GenericIdentity(user.Username, "TokenAuth"),
new[] {
new Claim("ID", user.ID.ToString())
}
);
var securityToken = handler.CreateToken(new SecurityTokenDescriptor
{
Issuer = TokenAuthOption.Issuer,
Audience = TokenAuthOption.Audience,
SigningCredentials = TokenAuthOption.SigningCredentials,
Subject = identity,
Expires = expires
});
return handler.WriteToken(securityToken);
}
[HttpGet]
[Authorize("Bearer")]
public string GetUserInfo()
{
var claimsIdentity = User.Identity as ClaimsIdentity;
return JsonConvert.SerializeObject(new RequestResult
{
State = RequestState.Success,
Data = new
{
UserName = claimsIdentity.Name
}
});
}
}
public class User
{
public Guid ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
public static class UserStorage
{
public static List<User> Users { get; set; } = new List<User> {
new User {ID=Guid.NewGuid(),Username="user1",Password = "user1psd" },
new User {ID=Guid.NewGuid(),Username="user2",Password = "user2psd" },
new User {ID=Guid.NewGuid(),Username="user3",Password = "user3psd" }
};
}
}
4.2Angular2端
4.2.1創(chuàng)建View Model
在wwwroot/app下創(chuàng)建一個目錄:_model, 并添加一個Typescript文件RequestResult.ts,內(nèi)容應該是這樣。
export class RequestResult {
State: number;
Msg: string;
Data: Object;
}
4.2.2創(chuàng)建Service
在wwwroot/app下創(chuàng)建一個目錄:_services,并添加一個Typescript文件auth.service.ts,內(nèi)容應該是這樣。
import { Injectable } from "@angular/core";
import { Headers, Http } from "@angular/http";
import "rxjs/add/operator/toPromise";
import { RequestResult } from "../_model/RequestResult";
@Injectable()
export class AuthService {
private tokeyKey = "token";
private token: string;
constructor(
private http: Http
) { }
login(userName: string, password: string): Promise<RequestResult> {
return this.http.post("/api/TokenAuth", { Username: userName, Password: password }).toPromise()
.then(response => {
let result = response.json() as RequestResult;
if (result.State == 1) {
let json = result.Data as any;
sessionStorage.setItem("token", json.accessToken);
}
return result;
})
.catch(this.handleError);
}
checkLogin(): boolean {
var token = sessionStorage.getItem(this.tokeyKey);
return token != null;
}
getUserInfo(): Promise<RequestResult> {
return this.authGet("/api/TokenAuth");
}
authPost(url: string, body: any): Promise<RequestResult> {
let headers = this.initAuthHeaders();
return this.http.post(url, body, { headers: headers }).toPromise()
.then(response => response.json() as RequestResult)
.catch(this.handleError);
}
authGet(url): Promise<RequestResult> {
let headers = this.initAuthHeaders();
return this.http.get(url, { headers: headers }).toPromise()
.then(response => response.json() as RequestResult)
.catch(this.handleError);
}
private getLocalToken(): string {
if (!this.token) {
this.token = sessionStorage.getItem(this.tokeyKey);
}
return this.token;
}
private initAuthHeaders(): Headers {
let token = this.getLocalToken();
if (token == null) throw "No token";
var headers = new Headers();
headers.append("Authorization", "Bearer " + token);
return headers;
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
}
本文件主要用來完成登錄以及登錄驗證工作,之后該service將可以被注入到Component中以便被Component調(diào)用。
注:主要的邏輯都應該寫到service中
4.2.3.創(chuàng)建Component
4.2.3.1.在wwwroot/app下創(chuàng)建一個目錄home,該目錄用來存放HomeComponent,home應擁有如下文件:
•home.component.ts
import { Component, OnInit } from "@angular/core";
import { AuthService } from "../_services/auth.service";
@Component({
moduleId: module.id,
selector: "my-home",
templateUrl: "view.html",
styleUrls: ["style.css"]
})
export class HomeComponent implements OnInit {
isLogin = false;
userName: string;
constructor(
private authService: AuthService
) { }
ngOnInit(): void {
this.isLogin = this.authService.checkLogin();
if (this.isLogin) {
this.authService.getUserInfo().then(res => {
this.userName = (res.Data as any).UserName;
});
}
}
}
查閱代碼,在@Component中指定了View以及style。
AuthService被在構(gòu)造方法中被注入了本Component,ngOnInit是接口OnInit的一個方法,他在Component初始化時會被調(diào)用。
•style.css
/*styles of this view*/
本例中沒有添加任何樣式,如有需要可以寫在這里。
•view.html
<div *ngIf="isLogin">
<h1>Hi <span>{{userName}}</span></h1>
</div>
<div *ngIf="!isLogin">
<h1>please login</h1>
<a routerLink="/login">Login</a>
</div>
*ngIf=""是Angular2 的其中一種標記語法,作用是當返回真時渲染該節(jié)點,完整教程請參閱官方文檔。
4.2.3.2.在wwwroot/app下創(chuàng)建目錄Login,該目錄用來存放LoginComponent,文件結(jié)構(gòu)類似于上一節(jié)。
•login.component.ts
import { Component } from "@angular/core";
import { Router } from '@angular/router';
import { AuthService } from "../_services/auth.service";
@Component({
moduleId: module.id,
selector: "my-login",
templateUrl: "view.html",
styleUrls: ["style.css"]
})
export class LoginComponent {
private userName: string;
private password: string;
constructor(
private authService: AuthService,
private router: Router
) { }
login() {
this.authService.login(this.userName, this.password)
.then(result => {
if (result.State == 1) {
this.router.navigate(["./home"]);
}
else {
alert(result.Msg);
}
});
}
}
•style.css
/*styles of this view*/
•view.html
<table>
<tr>
<td>userName:</td>
<td><input [(ngModel)]="userName" placeholder="useName:try type user1" /></td>
</tr>
<tr>
<td>userName:</td>
<td><input [(ngModel)]="password" placeholder="password:try type user1psd" /></td>
</tr>
<tr>
<td></td>
<td><input type="button" (click)="login()" value="Login" /></td>
</tr>
</table>
4.2.4.應用路由
路由是切換多頁面用的。
在wwwroot/app下新建一個Typescript文件,命名為app-routing.module.ts,內(nèi)容應該是這個樣子。
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { HomeComponent } from "./home/home.component";
import { LoginComponent } from "./login/login.component"
const routes: Routes = [
{ path: "", redirectTo: "/home", pathMatch: "full" },
{ path: "home", component: HomeComponent },
{ path: "login", component: LoginComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
接下來我們來應用這個路由,
打開app.module.ts,更新代碼如下:
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { HttpModule } from "@angular/http";
import { FormsModule } from "@angular/forms";
import { AppRoutingModule } from "./app-routing.module";
import { AuthService } from "./_services/auth.service";
import { AppComponent } from "./app.component";
import { HomeComponent } from "./home/home.component";
import { LoginComponent } from "./login/login.component";
@NgModule({
bootstrap: [AppComponent],
imports: [
BrowserModule,
HttpModule,
AppRoutingModule,
FormsModule
],
declarations: [
AppComponent,
HomeComponent,
LoginComponent
],
providers: [AuthService]
})
export class AppModule { }
NgModule和BrowserModule你可以理解為基礎(chǔ)模塊,必加的。
HttpModule是做http請求用的。
FormsModule是做雙向數(shù)據(jù)綁定用的,比如下面這樣的,如果想把數(shù)據(jù)從view更新到component,就必須加這個。
<input [(ngModel)]="userName" placeholder="useName:try type user1" />
AppRoutingModule即為我們剛才添加的路由文件。
AuthService是我們最早添加的service文件。
AppComponent是我們最初添加的那個app.component.ts里的那個component.
HomeComponent,LoginComponent同上。
最后我們再app.component.ts中添加路由錨點,
把template的值為 "<router-outlet></router-outlet>"
完整的代碼應該是這樣:
import { Component } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'my-app',
template: "<router-outlet></router-outlet>",
})
export class AppComponent {
}
router-outlet是路由錨點的關(guān)鍵詞。
至此,所有代碼完成,F(xiàn)5調(diào)試吧。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
VS2015 Update2 構(gòu)建 Android 程序問題匯總
這篇文章主要介紹了VS2015 Update2 構(gòu)建 Android 程序問題匯總的相關(guān)資料,需要的朋友可以參考下2016-07-07
在C#及.NET框架中使用StringBuilder類操作字符串的技巧
StringBuilder類在C#中一般被用來拼接字符串,而依托.NET Framework則可以實現(xiàn)添加替換移除等靈活的動態(tài)字符串操作,以下就來總結(jié)一下在C#及.NET框架中使用StringBuilder類操作字符串的技巧2016-05-05
ASP.NET Core如何添加統(tǒng)一模型驗證處理機制詳解
這篇文章主要給大家介紹了關(guān)于ASP.NET Core如何添加統(tǒng)一模型驗證處理機制的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用ASP.NET Core具有一定的參考學習價值,需要的朋友可以參考下2018-05-05
Visual Studio 2017正式版發(fā)布 亮點看這里
終于等到你,最強 IDE Visual Studio 2017 正式版發(fā)布,這篇文章主要為大家詳細解析了Visual Studio 2017正式版發(fā)布的細節(jié),亮點看這里2017-03-03
asp.net實現(xiàn)上傳圖片時判斷圖片的模式GRB或CMYK的方法
這篇文章主要介紹了asp.net實現(xiàn)上傳圖片時判斷圖片的模式GRB或CMYK的方法,涉及asp.net針對圖片的讀取及屬性操作相關(guān)技巧,需要的朋友可以參考下2016-07-07
巧妙使用JQuery Clone 添加多行數(shù)據(jù),并更新到數(shù)據(jù)庫的實現(xiàn)代碼
巧妙使用JQuery Clone 添加多行數(shù)據(jù),并更新到數(shù)據(jù)庫的實現(xiàn)代碼,需要的朋友可以參考下。2011-12-12

