本系列教程的主要內(nèi)容來源于 egghead.io get-started-with-angular 視頻教程,但針對視頻中的介紹的知識點做了適當(dāng)?shù)匮a充,建議有興趣的同學(xué)直接查看該視頻教程。
查看新版教程,請訪問 Angular 6.x 基礎(chǔ)教程
目錄
- 第一節(jié) - 基于 Angular CLI 新建項目
- 第二節(jié) - 創(chuàng)建簡單的組件
- ?第三節(jié) - 事件和模板引用
- 第四節(jié) - 事件進階
- 第五節(jié) - 注入服務(wù)
- 第六節(jié) - 使用 ngFor 指令
- 第七節(jié) - 使用 Input 裝飾器
- 第八節(jié) - 使用雙向綁定
- 第九節(jié) - 使用 Output 裝飾器
- 第十節(jié) - 組件樣式
第一節(jié) - 基于 Angular CLI 新建項目
安裝 Angular CLI (可選)
- 安裝 Angular CLI (可選)
$ npm install -g @angular/cli
- 檢測 Angular CLI 是否安裝成功
$ ng --version
使用 Angular CLI
- 新建項目
$ ng new angular4-fundamentals
- 啟動本地服務(wù)器
$ ng serve
若想進一步了解 Angular CLI 的詳細(xì)信息,請參考 Angular CLI 終極指南。
第二節(jié) - 創(chuàng)建簡單的組件
新建組件
$ ng generate component simple-form --inline-template --inline-style
# Or
$ ng g c simple-form -it -is # 表示新建組件,該組件使用內(nèi)聯(lián)模板和內(nèi)聯(lián)樣式
在命令行窗口運行以上命令后,將輸出以下內(nèi)容:
installing component
create src/app/simple-form/simple-form.component.spec.ts
create src/app/simple-form/simple-form.component.ts
update src/app/app.module.ts
即執(zhí)行上述操作后,創(chuàng)建了兩個文件:
- simple-form.component.spec.ts - 用于單元測試
- simple-form.component.ts - 新建的組件
除此之外,update src/app/app.module.ts 表示執(zhí)行上述操作后,Angular CLI 會自動幫我們更新 app.module.ts 文件。所更新的內(nèi)容是把我們新建的組件添加到 NgModule 的 declarations 數(shù)組中,具體如下:
@NgModule({
declarations: [
AppComponent,
SimpleFormComponent
],
...
})
export class AppModule { }
使用組件
AppComponent
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h3>{{title}}</h3>
<div>
<app-simple-form></app-simple-form>
</div>
`
})
export class AppComponent {
title = 'Hello, Angular';
}
SimpleFormComponent
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-simple-form',
template: `
<p>
simple-form Works!
</p>
`,
styles: []
})
export class SimpleFormComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
從生成的 SimpleFormComponent 組件中,我們發(fā)現(xiàn)組件的 selector 是 app-simple-form,而我們是使用以下命令創(chuàng)建該組件:
$ ng g c simple-form -it -is
即 Angular CLI 在創(chuàng)建組件時,自動幫我們添加了前綴。那為什么前綴是 app 呢?答案是在項目根目錄下的 .angular-cli.json 文件中,已經(jīng)默認(rèn)幫我們配置了默認(rèn)的前綴,具體如下:
{
...
"apps": [
{
"root": "src",
"outDir": "dist",
...
"prefix": "app",
...
}
],
}
當(dāng)然你可以根據(jù)實際需求,自行更改默認(rèn)的前綴配置。
第三節(jié) - 事件和模板引用
在 Angular 中,我們可以使用 (eventName) 語法,進行事件綁定。此外,可以使用 #variableName 的語法,定義模板引用。具體示例如下:
SimpleFormComponent
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'app-simple-form',
template: `
<div>
<input #myInput type="text">
<button (click)="onClick(myInput.value)">點擊</button>
</div>
`,
styles: []
})
export class SimpleFormComponent implements OnInit {
onClick(value) {
console.log(value);
}
ngOnInit() {}
}
需要注意的是,若我們改變綁定的表達式為 (click)="onClick(myInput)" ,當(dāng)我們點擊按鈕時,控制臺輸出的結(jié)果是:
<input type="text">
通過該輸出結(jié)果,我們可以知道 #variableName 語法,我們獲取的對象是對應(yīng) DOM 元素的引用。
第四節(jié) - 事件進階
獲取鼠標(biāo)事件
在第三節(jié)的示例中,假如我們需要獲取鼠標(biāo)事件,那應(yīng)該怎么辦呢?這時,我們可以引入 $event 變量,具體如下:
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'app-simple-form',
template: `
<div>
<input #myInput type="text">
<button (click)="onClick($event, myInput.value)">點擊</button>
</div>
`,
styles: []
})
export class SimpleFormComponent implements OnInit {
onClick(event, value) {
console.log(event);
console.log(value);
}
ngOnInit() {}
}
成功運行以上代碼,當(dāng)我們點擊按鈕時,控制臺將輸出:
MouseEvent {isTrusted: true, screenX: 180, screenY: 207, clientX: 165,
clientY: 75…}
需要注意的是,參數(shù)名一定要使用 $event ,否則無法獲取正確的鼠標(biāo)事件。此外,onClick($event, myInput.value) 表達式中,$event 的順序是任意的,如:
<button (click)="onClick(myInput.value, $event)">點擊</button>
當(dāng) Angular 在調(diào)用我們的事件處理函數(shù)時,會自動幫我們處理調(diào)用的參數(shù)。$event 自動映射為觸發(fā)的事件,與我們 Provider 中 Token 的作用類似。除了監(jiān)聽鼠標(biāo)事件外,我們還可以監(jiān)聽鍵盤事件。
獲取鍵盤事件
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'app-simple-form',
template: `
<div>
<input #myInput type="text" (keydown.enter)="onEnter($event, myInput.value)">
<button (click)="onClick($event, myInput.value)">點擊</button>
</div>
`,
styles: []
})
export class SimpleFormComponent implements OnInit {
// ...
onEnter(event, value) {
console.log(event);
console.log(value);
}
}
以上代碼中, (keydown.enter)="onEnter($event, myInput.value)" 表達式表示我們監(jiān)聽鍵盤 enter 鍵的按下事件,當(dāng)我們按下鍵盤的 enter 鍵時,將會調(diào)用組件類中定義的 onEnter() 方法。我們同樣也可以通過 $event 來獲取 KeyboardEvent 對象。
第五節(jié) - 注入服務(wù)
新建服務(wù)
$ ng g s mail
在命令行窗口運行以上命令后,將輸出以下內(nèi)容:
installing service
create src/app/mail.service.spec.ts
create src/app/mail.service.ts
WARNING Service is generated but not provided, it must be provided to be used
即執(zhí)行上述操作后,創(chuàng)建了兩個文件:
- mail.service.spec.ts - 用于單元測試
- mail.service.ts - 新建的服務(wù)
除此之外,WARNING Service is generated but not provided,... 表示執(zhí)行上述操作后,Angular CLI 只會幫我們創(chuàng)建 MailService 服務(wù),不會自動幫我們配置該服務(wù)。
配置服務(wù)
import {MailService} from "./mail.service";
@NgModule({
...
providers: [MailService],
bootstrap: [AppComponent]
})
export class AppModule { }
更新服務(wù)
import { Injectable } from '@angular/core';
@Injectable()
export class MailService {
message: string ='該消息來自MailService';
constructor() { }
}
使用服務(wù)
import { Component } from '@angular/core';
import {MailService} from "./mail.service";
@Component({
selector: 'app-root',
template: `
<h3>{{title}}</h3>
<div>
<app-simple-form></app-simple-form>
{{mailService.message}}
</div>
`
})
export class AppComponent {
title = 'Hello, Angular';
constructor(private mailService: MailService) {}
}
除了使用 constructor(private mailService: MailService) 方式注入服務(wù)外,我們也可以使用 Inject 裝飾器來注入 MailService 服務(wù):
import {Component, Inject} from '@angular/core';
@Component({...})
export class AppComponent {
title = 'Hello, Angular';
constructor(@Inject(MailService) private mailService) {}
}
不過對于 Type 類型(函數(shù)類型) 的對象,我們一般使用 constructor(private mailService: MailService) 方式進行注入。而 Inject 裝飾器一般用來注入非 Type 類型的對象。
使用Inject裝飾器
AppModule
@NgModule({
...
providers: [
MailService,
{provide: 'apiUrl', useValue: 'https://jsonplaceholder.typicode.com/'}
],
bootstrap: [AppComponent]
})
export class AppModule { }
AppComponent
@Component({
selector: 'app-root',
template: `
<h3>{{title}}</h3>
<div>
<app-simple-form></app-simple-form>
{{mailService.message}}
<p>API_URL: {{apiUrl}}</p>
</div>
`
})
export class AppComponent {
title = 'Hello, Angular';
constructor(
@Inject(MailService) private mailService,
@Inject('apiUrl') private apiUrl
) {}
}
第六節(jié) - 使用 ngFor 指令
在 Angular 中我們可以使用 ngFor 指令來顯示數(shù)組中每一項的信息。
使用 ngFor 指令
更新 MailService 服務(wù)
import { Injectable } from '@angular/core';
@Injectable()
export class MailService {
messages: string[] = [
'天之驕子,加入修仙之路群',
'Shadows,加入修仙之路群',
'Keriy,加入修仙之路群'
];
}
更新 AppComponent 組件
import {Component} from '@angular/core';
import {MailService} from "./mail.service";
@Component({
selector: 'app-root',
template: `
<h3>{{title}}</h3>
<ul>
<li *ngFor="let message of mailService.messages; index as i;">
{{i}} - {{message}}
</li>
</ul>
`
})
export class AppComponent {
title = 'Hello, Angular';
constructor(private mailService: MailService) {}
}
在 AppComponent 組件的模板中,我們使用 let item of items; 語法迭代數(shù)組中的每一項,另外我們使用 index as i 用來訪問數(shù)組中每一項的索引值。除了 index 外,我們還可以獲取以下的值:
- first: boolean - 若當(dāng)前項是可迭代對象的第一項,則返回 true
- last: boolean - 若當(dāng)前項是可迭代對象的最后一項,則返回 true
- even: boolean - 若當(dāng)前項的索引值是偶數(shù),則返回 true
- odd: boolean - 若當(dāng)前項的索引值是奇數(shù),則返回 true
需要注意的是,*ngFor 中的 * 號是語法糖,表示結(jié)構(gòu)指令。因為該語法最終會轉(zhuǎn)換成:
<ng-template ngFor let-item [ngForOf]="items" let-i="index">
<li>...</li>
</ng-template>
除了 *ngFor 外,常用的結(jié)構(gòu)指令還有 *ngIf、*ngSwitchCase 指令。
第七節(jié) - 使用 Input 裝飾器
為了讓我們能夠開發(fā)更靈活的組件,Angular 為我們提供了 Input 裝飾器,用于定義組件的輸入屬性。
使用 Input 裝飾器
更新 SimpleFormComponent 組件
import {Component, OnInit,Input} from '@angular/core';
@Component({
selector: 'app-simple-form',
template: `
<div>
{{message}}
<input #myInput type="text" (keydown.enter)="onEnter($event, myInput.value)">
<button (click)="onClick($event, myInput.value)">點擊</button>
</div>
`,
styles: []
})
export class SimpleFormComponent implements OnInit {
@Input() message: string;
// ...
}
更新 AppComponent 組件
import {Component} from '@angular/core';
import {MailService} from "./mail.service";
@Component({
selector: 'app-root',
template: `
<h3>{{title}}</h3>
<app-simple-form *ngFor="let message of mailService.messages;"
[message]="message">
</app-simple-form>
`
})
export class AppComponent {
title = 'Hello, Angular';
constructor(private mailService: MailService) {}
}
在 AppComponent 組件模板中,我們使用 [message]="message" 屬性綁定的語法,實現(xiàn)數(shù)據(jù)傳遞。即把數(shù)據(jù)從 AppComponent 組件,傳遞到 SimpleFormComponent 組件中。
需要注意的是,當(dāng) SimpleFormComponent 組件類的屬性名稱不是 message 時,我們需要告訴 Angular 如何進行屬性值綁定,具體如下:
export class SimpleFormComponent implements OnInit {
@Input('message') msg: string;
// ...
}
不過一般不推薦這樣做,盡量保持名稱一致。
第八節(jié) - 使用雙向綁定
使用過 AngularJS 1.x 的同學(xué),應(yīng)該很熟悉 ng-model 指令,通過該指令我們可能方便地實現(xiàn)數(shù)據(jù)的雙向綁定。而在 Angular 中,我們是通過 ngModel 指令,來實現(xiàn)雙向綁定。
使用雙向綁定
引入 FormsModule
import {FormsModule} from "@angular/forms";
@NgModule({
// ...
imports: [
BrowserModule,
FormsModule
],
// ...
})
export class AppModule { }
使用 ngModel 指令
@Component({
selector: 'app-simple-form',
template: `
<div>
{{message}}
<input #myInput type="text" [(ngModel)]="message">
<button (click)="onClick($event, myInput.value)">點擊</button>
</div>
`,
styles: []
})
export class SimpleFormComponent implements OnInit { // ...}
上面示例中,我們使用 [(ngModel)]="message" 語法實現(xiàn)數(shù)據(jù)的雙向綁定。該語法也稱作 Banana in the Box 語法,即香蕉在盒子里 (比較形象生動,記憶該語法)。

除了使用雙向綁定,我們也可以通過 ngModel 指令,實現(xiàn)單向數(shù)據(jù)綁定,如 [ngModel]="message"。
第九節(jié) - 使用 Output 裝飾器
Output 裝飾器的作用是用來實現(xiàn)子組件將信息,通過事件的形式通知到父級組件。
在介紹 Output 屬性裝飾器前,我們先來介紹一下 EventEmitter 這個幕后英雄:
let numberEmitter: EventEmitter<number> = new EventEmitter<number>();
numberEmitter.subscribe((value: number) => console.log(value));
numberEmitter.emit(10);
接下來我們來介紹如何使用 Output 裝飾器。
使用 Output 裝飾器
更新 SimpleFormComponent 組件
import {Component, OnInit, Input, Output, EventEmitter} from '@angular/core';
@Component({
selector: 'app-simple-form',
template: `
<div>
{{message}}
<input #myInput type="text" [(ngModel)]="message">
<button (click)="update.emit({text: message})">更新</button>
</div>
`,
styles: []
})
export class SimpleFormComponent implements OnInit {
@Input() message: string;
@Output() update = new EventEmitter<{text: string}>();
ngOnInit() { }
}
更新 MailService 服務(wù)
import {Injectable} from '@angular/core';
@Injectable()
export class MailService {
messages: Array<{id: number, text: string}> = [
{id: 0, text: '天之驕子,加入修仙之路群'},
{id: 1, text: 'Shadows,加入修仙之路群'},
{id: 2, text: 'Keriy,加入修仙之路群'}
];
update(id, text) {
this.messages = this.messages.map(msg => {
return msg.id === id ? {id, text} : msg;
});
}
}
更新 AppComponent 組件
import {Component} from '@angular/core';
import {MailService} from "./mail.service";
@Component({
selector: 'app-root',
template: `
<h3>{{title}}</h3>
<ul>
<li *ngFor="let message of mailService.messages;">
{{message.text}}
</li>
</ul>
<app-simple-form *ngFor="let message of mailService.messages;"
[message]="message.text"
(update)="onUpdate(message.id, $event.text)">
</app-simple-form>
`
})
export class AppComponent {
title = 'Hello, Angular';
onUpdate(id, text) {
this.mailService.update(id, text);
}
constructor(private mailService: MailService) {}
}
上面示例中,我們?nèi)匀皇褂?(eventName) 事件綁定的語法,監(jiān)聽我們自定義的 update 事件。當(dāng)在 SimpleFormComponent 組件中修改 input 輸入框的文本消息后,點擊更新按鈕,將會調(diào)用 AppComponent 組件類中的 onUpdate() 方法,更新對應(yīng)的信息。
第十節(jié) - 組件樣式
在 Angular 中,我們可以在設(shè)置組件元數(shù)據(jù)時通過 styles 或 styleUrls 屬性,來設(shè)置組件的內(nèi)聯(lián)樣式和外聯(lián)樣式。
使用 styles 屬性
import {Component, OnInit, Input, Output, EventEmitter} from '@angular/core';
@Component({
selector: 'app-simple-form',
template: `
...
`,
styles: [`
:host { margin: 10px; }
input:focus { font-weight: bold;}
`
]
})
export class SimpleFormComponent implements OnInit {
@Input() message: string;
@Output() update = new EventEmitter<{text: string}>();
ngOnInit() {}
}
上面示例中 :host 表示選擇宿主元素,即 AppComponent 組件模板中的 app-simple-form 元素。
用過 AngularJS 1.x 的同學(xué),對 ng-class 應(yīng)該很熟悉,通過它我們能夠根據(jù)條件,為元素動態(tài)的添加或移除對應(yīng)的樣式。在 Angular 中,對應(yīng)的指令是 ngClass 。接下來我們來看一下,ngClass 指令的具體應(yīng)用。
使用 ngClass 指令
ngClass 指令接收一個對象字面量,對象的 key 是 CSS class 的名稱,value 的值是 truthy/falsy 的值,表示是否應(yīng)用該樣式。
@Component({
selector: 'app-simple-form',
template: `
<div>
{{message}}
<input #myInput
type="text"
[(ngModel)]="message"
[ngClass]="{mousedown: isMousedown}"
(mousedown)="isMousedown = true"
(mouseup)="isMousedown = false"
(mouseleave)="isMousedown = false"
>
<button (click)="update.emit({text: message})">更新</button>
</div>
`,
styles: [`
:host { margin: 10px; }
.mousedown { border: 2px solid green; }
input:focus { font-weight: bold; outline: none;}
`
]
})
export class SimpleFormComponent implements OnInit {
isMousedown: boolean;
// ...
}
ngClass 指令用法
<!-- 使用布爾值 -->
<div [ngClass]="{bordered: false}">This is never bordered</div>
<div [ngClass]="{bordered: true}">This is always bordered</div>
<!-- 使用組件實例的屬性 -->
<div [ngClass]="{bordered: isBordered}">
Using object literal. Border {{ isBordered ? "ON" : "OFF" }}
</div>
<!-- 樣式名包含'-' -->
<div[ngClass]="{'bordered-box': false}">
Class names contains dashes must use single quote
</div>
<!-- 使用樣式列表 -->
<div class="base" [ngClass]="['blue', 'round']">
This will always have a blue background and round corners
</div>
除了 ngClass 指令外,Angular 還為我們提供了 ngStyle 指令。
使用 ngStyle 指令
ngStyle 指令讓我們可以方便得通過 Angular 表達式,設(shè)置 DOM 元素的 CSS 屬性。
ngStyle 指令用法
<div [ngStyle]="{color: 'white', 'background-color': 'blue'}">
Uses fixed white text on blue background
</div>
需要注意的是, background-color 需要使用單引號,而 color 不需要。這其中的原因是,ng-style 要求的參數(shù)是一個 Javascript 對象,color 是一個有效的 key,而 background-color 不是一個有效的 key ,所以需要添加 ''。
對于一些場合,我們也可以直接利用 Angular 屬性綁定的語法,來快速設(shè)置元素的樣式。
- 設(shè)置元素的背景顏色
<div [style.background-color="'yellow'"]>
Use fixed yellow background
</div>
- 設(shè)置元素的字體大小
<!-- 支持單位: px | em | %-->
<div>
<span [ngStyle]="{color: 'red'}" [style.font-size.px]="fontSize">
Red Text
</span>
</div>
我有話說
除了本系列教程外,還有其它入門的資料么?
本系列教程的主要目的是讓初學(xué)者對 Angular 的相關(guān)基礎(chǔ)知識,有一定的了解。除了本系列教程外,初學(xué)者還可以參考以下教程: