前言
1、TypeScript 是一種由微軟開發的自由和開源的編程語言,它是JavaScript的一個超集,擴展了JavaScript的語法,是Angular 官方推薦的前端開發語言。
2、Angular 2 應用可以使用 TypeScript 編寫,也可以使用純 JavaScript 來編寫,但一般會選擇 TypeScript ,主要是因爲:
TypeScript 增強了 JavaScript 的類型、類和接口,模仿強類型語言,若你是一個 C# 或者 Java 開發者,你可能會更喜歡使用這種強類型語言來
開發應用。一些可用於開發TypeScript的IDE(例如Atom),提供了代碼提示和自動補全功能,並提示語法或類型錯誤,可以像服務器端那樣進行客戶
端開發。
4、Angular2應用由模塊和組件構成,通過ng命令創建工程後,默認生成模塊和組件ts文件,以及測試相關ts文件和組件相關的html模板、樣式等文件。 這幾個文件的關係是從屬關係:
|--app.module.ts(模塊)
|--app.component.ts(組件)
|--app.component.html(HTML模板)
|--app.component.css(CSS樣式表)
一個模塊可以有多個組件,一個組件也可以有多個樣式表,但只有一個HTML模板。
-----------------------------------------------------------------------------------------------------------------------------------------------------
Angular2提供了大量內置模塊、組件、服務、指令等。
模塊:
...... 模塊就是將一系列組件、指令、服務等整合封裝到一起,提供一個完整的功能。
...... 模塊在其自身的作用域裏執行,而不是在全局作用域裏,這意味着定義在一個模塊裏的變量,函數,類等等在模塊外部是不可見的,但可使用export導出該模塊,其它模塊
就可使用import導入該模塊,這樣其它模塊就可使用該模塊導出的類,接口,變量,方法等。
...... 每個應用會有一個根模塊,按照約定,它的類名叫做AppModule,放在app.module.ts文件中,使用根模塊啓動應用。
每個根模塊會有一個根組件,默認是app.component.ts,名字是app-root。
查看項目目錄中的index.html,會發現有<app-root>Loading...</app-root>代碼,就是加載這個根模塊。
...... Angular2核心庫提供了NgModule、FormsModule、HttpModule等若干模塊。
...... 創建模塊 (以app.module.ts爲例,也可以創建自定義模塊)
import { BrowserModule } from '@angular/platform-browser'; //導入瀏覽器模塊,瀏覽器中運行的應用根模塊都需要導入該模塊
import { NgModule } from '@angular/core'; //導入核心庫NgModule模塊,每個模塊都需要導入該模塊
import { FormsModule } from '@angular/forms'; //導入表單模塊,在應用中使用表單時需要導入該模塊
import { HttpModule } from '@angular/http'; //導入http模塊,當需要進行http遠程請求時需要導入該模塊
import { AppComponent } from './app.component'; //導入自己創建的組件 *** (導入模塊from後帶有@,導入組件則沒有) ***
@NgModule({ //使用@NgModule()語句描述本模塊的元數據
declarations: [ //聲明屬於本模塊的組件
AppComponent
],
imports: [ //導入本模塊中要用到的模塊
BrowserModule,
FormsModule,
HttpModule
],
exports: [ //本模塊需要導出的一些模塊、組件、指令等
BrowserModule,
FormsModule,
HttpModule
],
providers: [], //聲明(注入)模塊中需要使用的服務提供者(類)
bootstrap: [AppComponent] //指定根模塊中的引導組件(一般只有一個引導組件),應用啓動過程中,會創建這個數組中的組件並插入到HTML中
})
export class AppModule { } //導出該(根)模塊,讓其他模塊可用到當前模塊中的類和變量等
組件:
...... 組件定義了一系列方法和屬性,並把HTML模板封裝起來,組件其實就是一個類。
定義的一段HTML代碼,並取個名字,就可以當作HTML標籤使用了。假如組件的名字是my-app,那麼就可以這樣使用:
<div>
<my-app><my-app>
</div>
...... 每個 Angular 應用都至少有一個根組件,整個應用由若干組件組成,而組件化(Component)則是 Angular 的核心靈魂。
...... 創建組件 (以app.component.ts爲例,也可以創建自定義組件)
import { Component } from '@angular/core'; //導入核心庫組件模塊,所有組件必須導入該模塊
@Component({ //使用@Component()來描述本組件的元數據
selector: 'app-root', //選擇創建的組件名字
templateUrl: './app.component.html', //指定HTML模板,使用相對路徑
styleUrls: ['./app.component.css'] //HTML模板使用的樣式表,可以有多個
providers: [] //聲明(注入)組件中需要使用的服務提供者(類)
})
export class AppComponent { //最後,使用export創建組件類,並暴露出去
title1 = 'Hello World - 111111111'; //在類中可以創建屬性和方法
title2 = 'Hello World - 222222222';
}
服務:
...... 服務定義了能夠被其它組件或指令調用的共享代碼,方便組件之間共享數據和方法,服務其實也是一個類,因此服務代碼和普通的類沒什麼區別。
但爲了區分出是一個服務類,文件名後綴是*.service.ts,類名格式爲XxxService。
...... 獲取數據的服務往往都是異步的,所以使用Promise去封裝獲取的數據,來模擬異步請求。
Promise是一個異步容器對象,是異步編程的一種解決方案(回調函數和事件),Promise 提供統一的 API,各種異步操作都可以用同樣的方法進行處理,從它可以獲取
異步操作的消息。可以通過Promise構造函數生成Promise實例:
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
...... 創建自定義服務
import {User} from "../classes/User";
import {USERS} from "../mock/user.data";
export class UserService {
getUsers(): User[] {
return Promise.resolve(USERS);
}
}
getUsers函數返回值是一個數組,數組元素是User類的實例,所以使用了getUsers(): User[],還有因爲我們要異步請求獲取數據,所以使用了Promise。
...... 如何使用自定義服務
例如在組件內使用服務,需要在文件頂部import指定路徑的服務類,然後在providers元數據類指定服務提供類。
import {Component, OnInit} from '@angular/core';
import {User} from "./classes/User";
import {UserService} from "./services/user.service";
@Component({
selector: 'my-app',
//template: '<h1>My First Angular2 Travel</h1>',
templateUrl: 'app/templates/main.html',
providers: [UserService]
})
export class AppComponent implements OnInit{
users: User[];
constructor(
private userService: UserService
){}
getUsersData() {
this.userService.getUsers().then(users => this.users = users);
}
ngOnInit() {
this.getUsersData();
}
}
路由:
路由的作用是建立URL與頁面的對應關係,根據不同的URL路徑匹配出相應的組件並顯示在outlet指定的區域。
例如:
http://localhost:4200/all-people 對應顯示allPeople頁面
http://localhost:4200/first-come 對應顯示firstCome頁面
1、新建一個app.toutes.ts根路由配置文件,創建根路由配置(rootRouterConfig),其內容爲:
// 需要從路由模塊中引入Routes類
import { Routes } from '@angular/router';
// 對於需要用url鏈接的組件,需要從文件中導入
import { AllPeopleComponent } from './all-people/all-people.component';
import { HomePageComponent } from './home-page/home-page.component';
//根路由定義爲一個數組,數組對象一般有path(對應url)和component(對應組件)兩個屬性,URL帶上相對路徑,找到後顯示
//對應的組件
export const rootRouterConfig: Routes = [
{
path: 'first-come', //對應 http://localhost:4200/first-come
component: HomePageComponent
},
{
path: 'all-people', //對應 http://localhost:4200/all-people
component: AllPeopleComponent
},
]
2、在根模塊AppModule對應app.module.ts文件中創建根路由模塊(rootRouterModule):
根路由模塊包含了路由需要使用的服務,它以路由配置爲參數,調用RouterModule.forRoot()方法來創建。
// 在根模塊中導入路由配置 和 路由模塊(需要使用該模塊的forRoot方法)
import { rootRouterConfig } from './app.routes'; // 注意沒有.ts
import { RouterModule } from '@angular/router';
/*
調用RouterModule.forRoot(config, 策略)方法創建根路由模塊rootRouterModule
根路由模塊提供兩種路由策略:PathLocationStrategy(默認策略)和HashLocationStrategy,不指定策略參數時使用默認策略
PathLocationStrategy路由策略需要一個base路徑,設置base路徑有2種方式,最簡單的是在index.html中head標籤內設置<base>,例子:
<head>
<base href="/"> //表示設置應用基礎 (根) 路徑,這樣網頁上的所有相對路徑在鏈接時,都會在前面加上基礎路徑指向的地址
......
</head>
使用HashLocationStrategy創建根路由模塊,例子:
const rootRouterModule: ModuleWithProviders = RouterModule.forRoot(rootRouterConfig, {useHash: true});
*/
const rootRouterModule: ModuleWithProviders = RouterModule.forRoot(rootRouterConfig);
@NgModule({
declarations: [],
// 導入路由模塊
imports: [rootRouterModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
3、使用RouterOutlet指令,添加組件存放的區域:
該指令在頁面預留一塊區域,當url改變時,匹配路由配置中的path,匹配成功後就將與path對應的component加載到這個區域裏內顯示。
例如:
<div style="font-size:20px;">
<router-outlet name="left"></router-outlet>
</div>
<div style="color:red;">
<router-outlet name="right"></router-outlet>
</div>
這裏name對應的left和right必須和模塊ts文件裏面的outlet相對應。
然後在rootRouterConfig根路由配置文件內將對應路徑的組件指定outlet屬性,對應代碼:
export const rootRouterConfig: Routes = [
{
path: 'first-come', //對應 http://localhost:4200/first-come
component: HomePageComponent,
outlet:'left'
},
{
path: 'all-people', //對應 http://localhost:4200/all-people
component: AllPeopleComponent,
outlet:'right'
},
]
到目前爲止,一個簡單的路由就基本配置完成了,只要在url中輸入正確的path,就能在outlet指定的區域顯示對應的組件。但是,跳轉頁面時不應手動修改url,因此需要通過頁面操作(點擊事件、延遲等方式)改變URL,實現path對應組件顯示,這就是下一節講解的路由跳轉。
4、使用RouterLink指令,實現路由跳轉:
正如第3節所說,路由跳轉是爲了避免手動輸入改變url,最常見的就是點擊事件改變URL實現路由跳轉,然後在對應區域顯示組件。路由跳轉有
兩種方式。
指令跳轉:使用RouterlLink指令,接收連接參數爲數組,當事件觸發時,根據數組內參數去從路由配置查找對應的path和對應的組件並顯示。
例如:
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a [routerLink]="['/all-people']">allPeople</a></li>
<li><a [routerLink]="['/first-come']">firstCome</a></li>
<li><a [routerLink]="['/last-leave']">lastLeave</a></li>
</ul>
</div>
RouterLink的強大之處是他可以被應用到任何HTML元素上,使得頁面跳轉不需要依賴超鏈接,例如:
<a routerLink="/form">表單</a>
<button [routerLink]="['/form']">表單</button>
代碼跳轉:
指令跳轉無法做到動態響應,因此使用諸如延遲跳轉等事件時需要使用代碼跳轉。navigate和navigateByUrl是Router類的方法,用來跳轉路由。
例如:this.router.navigate(['/exampledetail'],{queryParams:{'name':'yxman'}}); //route是ActivatedRoute的實例,使用需要導入ActivatedRoute
方法定義:
navigate(commands: any[], extras?: NavigationExtras) : Promise`<boolean>`
接口定義:
interface NavigationExtras {
relativeTo : ActivatedRoute
queryParams : Params
fragment : string
preserveQueryParams : boolean
preserveFragment : boolean
skipLocationChange : boolean
replaceUrl : boolean
}
this.router.navigate(['user', 1]); //以根路由爲起點跳轉
this.router.navigate(['user', 1],{relativeTo: route}); //相對當前路由跳轉
this.router.navigate(['user', 1],{ queryParams: { id: 1 } }); //路由中傳參數 /user/1?id=1
this.router.navigate(['view', 1], { preserveQueryParams: true }); //默認值爲false,設爲true,保留之前路由中的查詢參數/user?id=1 to /view?id=1
this.router.navigate(['user', 1],{ fragment: 'top' }); //路由中錨點跳轉 /user/1#top
this.router.navigate(['/view'], { preserveFragment: true }); //默認值爲false,設爲true,保留之前路由中的錨點/user/1#top to /view#top
this.router.navigate(['/user',1], { skipLocationChange: true }); //默認值爲false,設爲true路由跳轉時瀏覽器中的url會保持不變,但是傳入的參數依然有效
this.router.navigate(['/user',1], { replaceUrl: true }); //未設置時默認爲true,設置爲false路由不會進行跳轉
5、路由傳遞參數:
單一參數: <a routerLink=["/exampledetail",id]></a> 或 this.router.navigate(['/exampledetail',id]); 或 this.router.navigateByUrl('/exampledetail/id');
多個參數: <a routerLink=["/exampledetail",{queryParams:'id':'1','name':'yxman'}]></a>
或 this.router.navigate(['/exampledetail'],{queryParams:{'name':'yxman'}});
或 this.router.navigateByUrl('/exampledetail',{queryParams:{'name':'yxman'}});
6、路由接收參數:
ngOnInit(){
this.data = this.route.snapshot.params['id'];
this.activeRoute.queryParams.subscribe(params => {
this.id = params['id'];
this.name = params['name'];
});
}
後續研究:子路由
指令:
...... 在Angular2中有三種類型的指令(Directive)
組件指令:擁有模板的指令。
結構型指令:通過添加和移除DOM元素來改變DOM結構的指令。例如Angluar2默認提供的指令:NgFor, NgIf …
屬性型指令:改變元素顯示樣式和行爲的指令。例如Angluar2默認提供的指令:NgStyle …
...... 創建自定義指令(以創建屬性型指令爲例:鼠標在元素上懸停時,改變元素背景色)
屬性型指令至少需要一個帶有@Directive裝飾器修飾的控制器類,@Directive裝飾器指定了一個選擇器名稱,用於指出與此指令相關聯的屬性名字。
import { Directive, ElementRef, Renderer } from '@angular/core'; //從Angular Core庫中導入需要使用的模塊
@Directive({
//@Directive裝飾器需要一個css選擇器來指定selector的值,以便Angular編譯器從模版中識別出關聯到這個指令的HTML
selector: '[prefixHightlight]'
})
//在@Directive元數據後面,聲明並導出指令對應的控制器類 HighlightDirective,HighlightDirective類包含了prefixHightlight指令的工作邏輯
export class HighlightDirective {
//在 HighlightDirective 類的構造函數中注入 ElementRef 和 Renderer 模塊的實例
//ElementRef 是一個服務,它賦予了我們直接訪問 DOM 元素的能力。注入 ElementRef 的目的在於讓指令可以引用到真實的 DOM 元素
//Renderer 可以讓 Hightlight 類裏面的邏輯代碼能夠正確的渲染 DOM 元素的樣式
constructor(elem: ElementRef, renderer: Renderer) {
renderer.setElementStyle(el.nativeElement, 'backgroundColor', 'red');
}
}
...... 如何使用自定義指令
創建一個組件模板文件app.component.html:
<h1>Angualr2 Directive Study</h1>
<article prefixHightlight>hover me</article>
定義一個組件app.component.ts:
import { Component, OnInit } from '@angualr/core';
@Component({
selector: 'prefix-app',
templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit {
constructor() {}
ngOnInit(): void {
console.log(`AppComponent has inited : ${ Date.now() }`);
}
}
聲明一個模塊app.module.ts,並在這個模塊中聲明自己定義的指令,以便Angualr在解析模板時,能正確識別自定義指令:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HighlightDirective } from './highlight.directive';
@NgModule({
imports: [ BrowserModule ],
declarations: [
AppComponent,
HighlightDirective
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
自定義的屬性型指令的運行原理:
Angular在編譯模板時,檢測到DOM元素上正在嘗試綁定某些屬性指令,但Angualr並不能識別這些非內置的屬性指令。
Angular就會嘗試在declarations 元數據中查找這個指令屬性,Angular 發現HighlightDirective指令後,接着就會去檢查對應的導入語句,從而找到 highlight.directive.ts 中
導出的類,進而服務宿主元素對應的行爲能力。
枚舉:
管道:
類:
類是一個抽象概念,組件、服務、指令等都是類。
-----------------------------------------------------------------------------------------------------------------------------------------------------
應用啓動流程
...... 加載根模塊。
...... 加載所需的其他模塊。
...... 創建引導組件。
...... 顯示組件內容。
-----------------------------------------------------------------------------------------------------------------------------------------------------
ng2項目的模塊體系
總體思路就是:
> 1.根模塊負責全局的路由。
> 2.核心模塊負責全局服務,也可以定義一些只在根模塊中使用的組件等,並只能由根模塊引入一次,不再導出。
> 3.共享模塊不做服務的提供,而是定義全局共享的組件等,以及幫助子模塊導入系統模塊,讓子模塊只需要導入此共享模塊就夠了。
> 4.子模塊內部可以細分自己的子路由到具體的子組件,以及提供自己的服務等。
> 5.除了頁面入口模塊(即除了根模塊外的具體業務模塊)之外的其他子模塊均考慮寫成惰性加載的模塊,以提升頁面引導的速度減少性能浪費。
> 6.當需要一個比較通用的全局服務時,可以將其加入CoreModule,也可以再創建一個僅被根模塊引入的特性模塊。進一步的,甚至可以將此模塊發佈到npm,這就需要更強的編碼能力和技術積累了。