本系列教程的主要內容來源于 egghead.io get-started-with-angular 視頻教程,但針對視頻中的介紹的知識點做了適當地補充,建議有興趣的同學直接查看該視頻教程。
查看新版教程,請訪問 Angular 6.x 基礎教程
目錄
- 第一節 - 基于 Angular CLI 新建項目
- 第二節 - 創建簡單的組件
- ?第三節 - 事件和模板引用
- 第四節 - 事件進階
- 第五節 - 注入服務
- 第六節 - 使用 ngFor 指令
- 第七節 - 使用 Input 裝飾器
- 第八節 - 使用雙向綁定
- 第九節 - 使用 Output 裝飾器
- 第十節 - 組件樣式
第一節 - 基于 Angular CLI 新建項目
安裝 Angular CLI (可選)
- 安裝 Angular CLI (可選)
$ npm install -g @angular/cli
- 檢測 Angular CLI 是否安裝成功
$ ng --version
使用 Angular CLI
- 新建項目
$ ng new angular4-fundamentals
- 啟動本地服務器
$ ng serve
若想進一步了解 Angular CLI 的詳細信息,請參考 Angular CLI 終極指南。
第二節 - 創建簡單的組件
新建組件
$ ng generate component simple-form --inline-template --inline-style
# Or
$ ng g c simple-form -it -is # 表示新建組件,該組件使用內聯模板和內聯樣式
在命令行窗口運行以上命令后,將輸出以下內容:
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
即執行上述操作后,創建了兩個文件:
- simple-form.component.spec.ts - 用于單元測試
- simple-form.component.ts - 新建的組件
除此之外,update src/app/app.module.ts
表示執行上述操作后,Angular CLI 會自動幫我們更新 app.module.ts
文件。所更新的內容是把我們新建的組件添加到 NgModule
的 declarations
數組中,具體如下:
@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
組件中,我們發現組件的 selector
是 app-simple-form
,而我們是使用以下命令創建該組件:
$ ng g c simple-form -it -is
即 Angular CLI 在創建組件時,自動幫我們添加了前綴。那為什么前綴是 app
呢?答案是在項目根目錄下的 .angular-cli.json
文件中,已經默認幫我們配置了默認的前綴,具體如下:
{
...
"apps": [
{
"root": "src",
"outDir": "dist",
...
"prefix": "app",
...
}
],
}
當然你可以根據實際需求,自行更改默認的前綴配置。
第三節 - 事件和模板引用
在 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)"
,當我們點擊按鈕時,控制臺輸出的結果是:
<input type="text">
通過該輸出結果,我們可以知道 #variableName
語法,我們獲取的對象是對應 DOM 元素的引用。
第四節 - 事件進階
獲取鼠標事件
在第三節的示例中,假如我們需要獲取鼠標事件,那應該怎么辦呢?這時,我們可以引入 $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() {}
}
成功運行以上代碼,當我們點擊按鈕時,控制臺將輸出:
MouseEvent {isTrusted: true, screenX: 180, screenY: 207, clientX: 165,
clientY: 75…}
需要注意的是,參數名一定要使用 $event
,否則無法獲取正確的鼠標事件。此外,onClick($event, myInput.value)
表達式中,$event
的順序是任意的,如:
<button (click)="onClick(myInput.value, $event)">點擊</button>
當 Angular 在調用我們的事件處理函數時,會自動幫我們處理調用的參數。$event
自動映射為觸發的事件,與我們 Provider
中 Token
的作用類似。除了監聽鼠標事件外,我們還可以監聽鍵盤事件。
獲取鍵盤事件
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)"
表達式表示我們監聽鍵盤 enter
鍵的按下事件,當我們按下鍵盤的 enter
鍵時,將會調用組件類中定義的 onEnter()
方法。我們同樣也可以通過 $event
來獲取 KeyboardEvent
對象。
第五節 - 注入服務
新建服務
$ ng g s mail
在命令行窗口運行以上命令后,將輸出以下內容:
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
即執行上述操作后,創建了兩個文件:
- mail.service.spec.ts - 用于單元測試
- mail.service.ts - 新建的服務
除此之外,WARNING Service is generated but not provided,...
表示執行上述操作后,Angular CLI 只會幫我們創建 MailService
服務,不會自動幫我們配置該服務。
配置服務
import {MailService} from "./mail.service";
@NgModule({
...
providers: [MailService],
bootstrap: [AppComponent]
})
export class AppModule { }
更新服務
import { Injectable } from '@angular/core';
@Injectable()
export class MailService {
message: string ='該消息來自MailService';
constructor() { }
}
使用服務
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)
方式注入服務外,我們也可以使用 Inject
裝飾器來注入 MailService
服務:
import {Component, Inject} from '@angular/core';
@Component({...})
export class AppComponent {
title = 'Hello, Angular';
constructor(@Inject(MailService) private mailService) {}
}
不過對于 Type
類型(函數類型) 的對象,我們一般使用 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
) {}
}
第六節 - 使用 ngFor 指令
在 Angular 中我們可以使用 ngFor
指令來顯示數組中每一項的信息。
使用 ngFor 指令
更新 MailService 服務
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;
語法迭代數組中的每一項,另外我們使用 index as i
用來訪問數組中每一項的索引值。除了 index
外,我們還可以獲取以下的值:
- first: boolean - 若當前項是可迭代對象的第一項,則返回 true
- last: boolean - 若當前項是可迭代對象的最后一項,則返回 true
- even: boolean - 若當前項的索引值是偶數,則返回 true
- odd: boolean - 若當前項的索引值是奇數,則返回 true
需要注意的是,*ngFor
中的 *
號是語法糖,表示結構指令。因為該語法最終會轉換成:
<ng-template ngFor let-item [ngForOf]="items" let-i="index">
<li>...</li>
</ng-template>
除了 *ngFor
外,常用的結構指令還有 *ngIf
、*ngSwitchCase
指令。
第七節 - 使用 Input 裝飾器
為了讓我們能夠開發更靈活的組件,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"
屬性綁定的語法,實現數據傳遞。即把數據從 AppComponent
組件,傳遞到 SimpleFormComponent
組件中。
需要注意的是,當 SimpleFormComponent
組件類的屬性名稱不是 message
時,我們需要告訴 Angular 如何進行屬性值綁定,具體如下:
export class SimpleFormComponent implements OnInit {
@Input('message') msg: string;
// ...
}
不過一般不推薦這樣做,盡量保持名稱一致。
第八節 - 使用雙向綁定
使用過 AngularJS 1.x 的同學,應該很熟悉 ng-model
指令,通過該指令我們可能方便地實現數據的雙向綁定。而在 Angular 中,我們是通過 ngModel
指令,來實現雙向綁定。
使用雙向綁定
引入 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"
語法實現數據的雙向綁定。該語法也稱作 Banana in the Box
語法,即香蕉在盒子里 (比較形象生動,記憶該語法)。
除了使用雙向綁定,我們也可以通過 ngModel
指令,實現單向數據綁定,如 [ngModel]="message"
。
第九節 - 使用 Output 裝飾器
Output
裝飾器的作用是用來實現子組件將信息,通過事件的形式通知到父級組件。
在介紹 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 服務
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) {}
}
上面示例中,我們仍然使用 (eventName)
事件綁定的語法,監聽我們自定義的 update
事件。當在 SimpleFormComponent
組件中修改 input
輸入框的文本消息后,點擊更新按鈕,將會調用 AppComponent
組件類中的 onUpdate()
方法,更新對應的信息。
第十節 - 組件樣式
在 Angular 中,我們可以在設置組件元數據時通過 styles
或 styleUrls
屬性,來設置組件的內聯樣式和外聯樣式。
使用 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 的同學,對 ng-class
應該很熟悉,通過它我們能夠根據條件,為元素動態的添加或移除對應的樣式。在 Angular 中,對應的指令是 ngClass
。接下來我們來看一下,ngClass
指令的具體應用。
使用 ngClass 指令
ngClass
指令接收一個對象字面量,對象的 key
是 CSS class 的名稱,value
的值是 truthy/falsy
的值,表示是否應用該樣式。
@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 表達式,設置 DOM 元素的 CSS 屬性。
ngStyle 指令用法
<div [ngStyle]="{color: 'white', 'background-color': 'blue'}">
Uses fixed white text on blue background
</div>
需要注意的是, background-color
需要使用單引號,而 color
不需要。這其中的原因是,ng-style
要求的參數是一個 Javascript
對象,color
是一個有效的 key
,而 background-color
不是一個有效的 key
,所以需要添加 ''
。
對于一些場合,我們也可以直接利用 Angular 屬性綁定的語法,來快速設置元素的樣式。
- 設置元素的背景顏色
<div [style.background-color="'yellow'"]>
Use fixed yellow background
</div>
- 設置元素的字體大小
<!-- 支持單位: px | em | %-->
<div>
<span [ngStyle]="{color: 'red'}" [style.font-size.px]="fontSize">
Red Text
</span>
</div>
我有話說
除了本系列教程外,還有其它入門的資料么?
本系列教程的主要目的是讓初學者對 Angular 的相關基礎知識,有一定的了解。除了本系列教程外,初學者還可以參考以下教程: