本系列教程的主要內容來源於 egghead.io get-started-with-angular 視頻教程,但針對視頻中的介紹的知識點作了適當地補充,建議有興趣的同窗直接查看該視頻教程。
另外建了個羣有興趣的朋友能夠加一下 QQ 羣:Angular 修仙之路 - 153742079 (羣名稱規則:城市 + 暱稱)
查看新版教程,請訪問 Angular 6.x 基礎教程
$ npm install -g @angular/cli
$ ng --version
$ ng new angular4-fundamentals
$ ng serve
若想進一步瞭解 Angular CLI 的詳細信息,請參考 Angular CLI 終極指南。css
$ ng generate component simple-form --inline-template --inline-style # Or $ ng g c simple-form -it -is # 表示新建組件,該組件使用內聯模板和內聯樣式
在命令行窗口運行以上命令後,將輸出如下內容:html
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
即執行上述操做後,建立了兩個文件:node
除此以外,update src/app/app.module.ts
表示執行上述操做後,Angular CLI 會自動幫咱們更新 app.module.ts
文件。所更新的內容是把咱們新建的組件添加到 NgModule
的 declarations
數組中,具體以下:git
@NgModule({ declarations: [ AppComponent, SimpleFormComponent ], ... }) export class AppModule { }
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'; }
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
,而咱們是使用如下命令建立該組件:github
$ ng g c simple-form -it -is
即 Angular CLI 在建立組件時,自動幫咱們添加了前綴。那爲何前綴是 app
呢?答案是在項目根目錄下的 .angular-cli.json
文件中,已經默認幫咱們配置了默認的前綴,具體以下:typescript
{ ... "apps": [ { "root": "src", "outDir": "dist", ... "prefix": "app", ... } ], }
固然你能夠根據實際需求,自行更改默認的前綴配置。shell
在 Angular 中,咱們可使用 (eventName)
語法,進行事件綁定。此外,可使用 #variableName
的語法,定義模板引用。具體示例以下:npm
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)"
,當咱們點擊按鈕時,控制檯輸出的結果是:json
<input type="text">
經過該輸出結果,咱們能夠知道 #variableName
語法,咱們獲取的對象是對應 DOM 元素的引用。bootstrap
在第三節的示例中,假如咱們須要獲取鼠標事件,那應該怎麼辦呢?這時,咱們能夠引入 $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
即執行上述操做後,建立了兩個文件:
除此以外,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
類型的對象。
@NgModule({ ... providers: [ MailService, {provide: 'apiUrl', useValue: 'https://jsonplaceholder.typicode.com/'} ], bootstrap: [AppComponent] }) export class AppModule { }
@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 ) {} }
在 Angular 中咱們可使用 ngFor
指令來顯示數組中每一項的信息。
import { Injectable } from '@angular/core'; @Injectable() export class MailService { messages: string[] = [ '天之驕子,加入修仙之路羣', 'Shadows,加入修仙之路羣', 'Keriy,加入修仙之路羣' ]; }
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
外,咱們還能夠獲取如下的值:
須要注意的是,*ngFor
中的 *
號是語法糖,表示結構指令。由於該語法最終會轉換成:
<ng-template ngFor let-item [ngForOf]="items" let-i="index"> <li>...</li> </ng-template>
除了 *ngFor
外,經常使用的結構指令還有 *ngIf
、*ngSwitchCase
指令。
爲了讓咱們可以開發更靈活的組件,Angular 爲咱們提供了 Input
裝飾器,用於定義組件的輸入屬性。
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; // ... }
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
指令,來實現雙向綁定。
import {FormsModule} from "@angular/forms"; @NgModule({ // ... imports: [ BrowserModule, FormsModule ], // ... }) export class AppModule { }
@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 屬性裝飾器前,咱們先來介紹一下 EventEmitter
這個幕後英雄:
let numberEmitter: EventEmitter<number> = new EventEmitter<number>(); numberEmitter.subscribe((value: number) => console.log(value)); numberEmitter.emit(10);
接下來咱們來介紹如何使用 Output
裝飾器。
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() { } }
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; }); } }
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
屬性,來設置組件的內聯樣式和外聯樣式。
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
指令接收一個對象字面量,對象的 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; // ... }
<!-- 使用布爾值 --> <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
指令讓咱們能夠方便得經過 Angular 表達式,設置 DOM 元素的 CSS 屬性。
<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>
若要引入第三方 UI 庫,能夠在 .angular-cli.json
文件中,配置對應的樣式文件地址,具體以下:
{ "apps": { "styles": [ "styles.css", "../node_modules/bootstrap/dist/css/bootstrap.min.css" ] } }
本系列教程的主要目的是讓初學者對 Angular 的相關基礎知識,有必定的瞭解。除了本系列教程外,初學者還能夠參考如下教程: