第一節:初識Angular-CLI
第二節:登陸組件的構建
第三節:創建一個待辦事項應用
第四節:進化!模塊化你的應用
第五節:多用戶版本的待辦事項應用
第六節:使用第三方樣式庫及模塊優化用
第七節:給組件帶來活力
Rx--隱藏在 Angular 中的利劍
Redux 你的 Angular 應用
第八節:查缺補漏大合集(上)
第九節:查缺補漏大合集(下)javascript
我寫文章的習慣是先試驗再理論,因此咱們接下來梳理下Angular2提供的動畫技能。仍是從最簡單的例子開始,一個很是簡單的模版:css
<div class="traffic-light"></div>複製代碼
一樣很是簡單的樣式(其實就是畫一個小黑塊):html
.traffic-light{
width: 100px;
height: 100px;
background-color: black;
}複製代碼
如今的效果就是這個樣子,如圖所示,一點都不酷啊,不要緊,咱們一點點來,越簡單的越容易弄懂概念。java
下面咱們爲組件添加一個animations的元數據描述:git
import {
Component,
trigger,
state,
style
} from '@angular/core';
@Component({
selector: 'app-playground',
templateUrl: './playground.component.html',
styleUrls: ['./playground.component.css'],
animations: [
trigger('signal', [
state('go', style({
'background-color': 'green'
}))
])
]
})
export class PlaygroundComponent {
constructor() { }
}複製代碼
咱們注意到animations中接受的是一個數組,這個數組裏面咱們使用了一個叫trigger的函數,trigger接受的第一個參數是觸發器的名字,第二個參數是一個數組。這個數組是由一種叫state的函數和叫transition的函數組成的。github
那麼什麼是state?state表示一種狀態,當這種狀態激活時,state所附帶的樣式就會附着在應用trigger的那個控件上。transition又是什麼呢?tranistion描述了一系列動畫的步驟,在狀態遷移時這些動畫步驟就會執行。
咱們如今的這個版本中暫時只有state而沒有transition,讓咱們先來看看效果,固然在能夠看到效果前咱們先要把這個trigger應用到某個控件上。那在咱們的例子裏就是模版中的那個div了。json
<div [@signal]="'go'" class="traffic-light">
</div>複製代碼
返回瀏覽器,你會發現那個小黑塊變成小綠塊了,如圖所示數組
這說明什麼?咱們的state的樣式附着在div上了。爲何呢?由於 [@signal]="'go'" 定義了trigger的狀態是go。但這一點也不酷是嗎?是的,暫時是這樣,仍是那句話,不要急。
接下來,咱們再加一個狀態 stop,在stop激活時咱們要把小方塊的背景色設爲紅色,那麼咱們須要把animations改爲下面的樣子:瀏覽器
animations: [
trigger('signal', [
state('go', style({
'background-color': 'green'
})),
state('stop', style({
'background-color':'red'
}))
])
]複製代碼
同時咱們須要給模板加兩個按鈕Go和Stop。如今的模版看起來是下面的樣子angular2
<div [@signal]="signal" class="traffic-light">
</div>
<button (click)="onGo()">Go</button>
<button (click)="onStop()">Stop</button>複製代碼
固然你看獲得,咱們點擊按鈕時須要處理對應的點擊事件。在這裏咱們但願點擊Go時,方塊變綠,點擊Stop時方塊變紅。若是要達成這個目的,咱們須要一個叫signal的成員變量,在點擊的處理函數中更改相應的狀態。
export class PlaygroundComponent {
signal: string;
constructor() { }
onGo(){
this.signal = 'go';
}
onStop(){
this.signal = 'stop';
}
}複製代碼
如今打開瀏覽器,試驗一下,咱們會發現點擊Go變綠,而點擊Stop變紅。可是仍是沒動起來啊,是的,這是由於咱們還沒加transition呢,咱們只需把animations改寫一下,你分別點Go和Stop就能看到動畫效果了。爲了讓效果更明顯一些,咱們爲兩種狀態指定一下高度。
import {
Component,
OnDestroy,
trigger,
state,
style,
transition,
animate
} from '@angular/core';
@Component({
selector: 'app-playground',
templateUrl: './playground.component.html',
styleUrls: ['./playground.component.css'],
animations: [
trigger('signal', [
state('void', style({
'transform':'translateY(-100%)'
})),
state('go', style({
'background-color': 'green',
'height':'100px'
})),
state('stop', style({
'background-color':'red',
'height':'50px'
})),
transition('void => *', animate(5000))
])
]
})
export class PlaygroundComponent {
signal: string;
constructor() { }
onGo(){
this.signal = 'go';
}
onStop(){
this.signal = 'stop';
}
}複製代碼
那麼 transition('* => *', animate(500))
這句什麼意思呢?前面那個 '* => *'
是一個狀態遷移表達式,*
表示任意狀態,因此這個表達式告訴咱們,只要有狀態的變化就會激發後面的動畫效果。後面的就是告訴Angular作500毫秒的動畫,這個動畫默認是從一個狀態過渡到另外一個狀態。如今你們打開瀏覽器體驗一下,分別點擊Go和Stop,會發現咱們的小方塊從一個正方形變成一個長方形,紅色變成綠色的過程。體驗完以後再來看這句話:動畫其實就是由若干個狀態組成,由transition定義狀態過渡的步驟。
那麼下面咱們介紹一個void 狀態(空狀態),爲何會有void狀態呢?其實剛剛咱們也體驗了,只不過沒有定義這個void 狀態而已。咱們在組件中並無給signal賦初始值,這就意味着一開始trigger的狀態就是void。咱們每每在實現進場或離場動畫時須要這個void狀態。void狀態就是描述沒有狀態值時的狀態。
animations: [
trigger('signal', [
state('void', style({
'transform':'translateY(-100%)'
})),
state('go', style({
'background-color': 'green',
'height':'100px'
})),
state('stop', style({
'background-color':'red',
'height':'50px'
})),
transition('* => *', animate(500))
])
]複製代碼
上面代碼定義了一個void狀態,並且樣式上有一個按Y軸作的-100%的位移,其實這就是一開始讓小方塊從場景外進入場景內,這樣就是實現了一種進場動畫,你們能夠在瀏覽器中試驗一下。
上面的咱們的實驗中,你會發現transition中有個animate函數,可能你認爲它就是指定一個動畫的時間的函數。它的身手可不止那麼簡單呢,咱們來仔細挖掘一下。
首先呢,咱們來對上面的代碼作一個小改造,把animations數組改爲下面的樣子:
animations: [
trigger('signal', [
state('void', style({
'transform':'translateY(-100%)'
})),
state('go', style({
'background-color': 'green',
'height':'100px'
})),
state('stop', style({
'background-color':'red',
'height':'50px'
})),
transition('* => *', animate('.5s 1s'))
])
]複製代碼
咱們其實只對animate中的參數作了一點小改動,就是把animate(500) 改爲animate('.5s 1s')。那麼.5s表示動畫過渡時間爲0.5秒(其實和上面設置的500毫秒是同樣的),1s表示動畫延遲1秒後播放。如今咱們打開瀏覽器,看看效果如何吧。
固然還有更狠的大招,這個字符串表達式還能夠變成 '.5s 1s ease-out',後面的這個ease-out是一種緩動函數,它是可讓動畫效果更真實的一種方式。
現實世界中物體照着必定節奏移動,並非一開始就移動很快的,也不多是一直勻速運動的。怎麼理解呢?當皮球往下掉時,首先是越掉越快,撞到地上後回彈,最終才又碰觸地板。而緩動函數可使動畫的過渡效果按照這樣的真實場景抽象出的對應函數來進行繪製。ease-out只是衆多的緩動函數的其中一種,咱們固然能夠指定其餘函數。
另外須要說明的一點是諸如ease-out只是真實函數的一個友好名稱,咱們固然能夠直接指定背後的函數:cubic-bezier(0, 0, 0.58, 1) 。咱們下個小例子不用這個ease-out,由於效果可能不是特別明顯,咱們找一個明顯的,使用 cubic-bezier(0.175, 0.885, 0.32, 1.275) 。如今咱們打開瀏覽器,你仔細觀察一下是否看到了小方塊回彈的效果
animations: [
trigger('signal', [
state('void', style({
'transform':'translateY(-100%)'
})),
state('go', style({
'background-color': 'green',
'height':'100px'
})),
state('stop', style({
'background-color':'red',
'height':'50px'
})),
transition('* => *', animate('.5s 1s cubic-bezier(0.175, 0.885, 0.32, 1.275)'))
])
]複製代碼
關於緩動函數的更多資料能夠訪問 easings.net/zh-cn 在這裏能夠看到各類函數的曲線和效果,以及cubic-bezier函數的各類參數
須要注意的一點是Angular2實現動畫的機制實際上是基於W3C的Web Animation標準,這個標準暫時沒法支持全部的cubic-bezier函數,只有部分函數被支持。這樣的話咱們若是要實現某些不被支持的函數怎麼辦呢?那就得有請咱們的關鍵幀出場了。
何謂關鍵幀?首先須要知道什麼是幀?百度百科給了定義:
幀——就是動畫中最小單位的單幅影像畫面,至關於電影膠片上的每一格鏡頭。在動畫軟件的時間軸上幀表現爲一格或一個標記。
關鍵幀——至關於二維動畫中的原畫。指角色或者物體運動或變化中的關鍵動做所處的那一幀。關鍵幀與關鍵幀之間的動畫能夠由軟件來建立,叫作過渡幀或者中間幀。
先來作一個小實驗,咱們把入場動畫改形成關鍵幀形式。
import {
Component,
OnDestroy,
trigger,
state,
style,
transition,
animate,
keyframes
} from '@angular/core';
@Component({
selector: 'app-playground',
templateUrl: './playground.component.html',
styleUrls: ['./playground.component.css'],
animations: [
trigger('signal', [
state('void', style({
'transform':'translateY(-100%)'
})),
state('go', style({
'background-color': 'green',
'height':'100px'
})),
state('stop', style({
'background-color':'red',
'height':'50px'
})),
transition('void => *', animate(5000, keyframes([
style({'transform': 'scale(0)'}),
style({'transform': 'scale(0.1)'}),
style({'transform': 'scale(0.5)'}),
style({'transform': 'scale(0.9)'}),
style({'transform': 'scale(0.95)'}),
style({'transform': 'scale(1)'})
]))),
transition('* => *', animate('.5s 1s cubic-bezier(0.175, 0.885, 0.32, 1.275)'))
])
]
})
export class PlaygroundComponent {
// clock = Observable.interval(1000).do(_=>console.log('observable created'));
signal: string;
constructor() { }
onGo(){
this.signal = 'go';
}
onStop(){
this.signal = 'stop';
}
}複製代碼
保存後返回瀏覽器,你應該能夠看到一個正方形由小變大的進場動畫。
如今咱們來分析一下代碼,這個入場動畫是5秒的時間,咱們給出6個關鍵幀,也就是0s,1s,2s,3s,4s和5s這幾個。對於每一個關鍵幀,咱們給出的樣式都是放縮,而放縮的比例逐漸加大,並且是先快後慢,也就是說咱們能夠模擬出緩動函數的效果。
若是咱們不光作放縮,並且在style中還指定位置的話,這個動畫就會出現邊移動邊變大的效果了。把入場動畫改爲下面的樣子試試看吧。
transition('void => *', animate(5000, keyframes([
style({'transform': 'scale(0)', 'padding': '0px'}),
style({'transform': 'scale(0.1)', 'padding': '50px'}),
style({'transform': 'scale(0.5)', 'padding': '100px'}),
style({'transform': 'scale(0.9)', 'padding': '120px'}),
style({'transform': 'scale(0.95)', 'padding': '135px'}),
style({'transform': 'scale(1)', 'padding': '140px'})
]))),複製代碼
最後的結果可能仍是不酷,可是這樣的話利用關鍵幀咱們若是結合好CSS樣式,就會作出比較複雜的動畫了。
咱們一直沒有提到的一點就是管道,雖然咱們的例子中沒有用到,但其實這是Angular 2中提供很是方便的一個特性。這個特性可讓咱們很快的將數據在界面上以咱們想要的格式輸出出來。仍是拿例子說話,好比咱們在頁面上顯示一個日期,先創建一個簡單的模版:
<p> Without Pipe: Today is {{ birthday }} </p>
<p> With Pipe: Today is {{ birthday | date:"MM/dd/yy" }} </p>複製代碼
再來創建對應的組件文件:
import { Component, OnDestroy } from '@angular/core';
@Component({
selector: 'app-playground',
templateUrl: './playground.component.html',
styleUrls: ['./playground.component.css']
})
export class PlaygroundComponent {
birthday = new Date();
constructor() { }
}複製代碼
上面的例子可能還沒太明顯,咱們 進一步改造一下模板:
<p> Without Pipe: Today is {{ birthday }} </p>
<p> With Pipe: Today is {{ birthday | date:"MM/dd/yy" }} </p>
<p>The time is {{ birthday | date:'shortTime' }}</p>
<p>The time is {{ birthday | date:'medium' }}</p>複製代碼
並且更牛的是多個Pipes能夠串起來使用,好比說上圖中最下面那個日期咱們但願把Dec大寫,就能夠這樣使用:
<p>The time is {{ birthday | date:'medium' | uppercase }}</p>複製代碼
那麼本身寫一個Pipe是怎樣的體驗呢?建立一個Pipe很是簡單,咱們來體會一下。首先建立一個 src/app/playground/trim-space.pipe.ts
的文件:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'trimSpace'
})
export class TrimSpacePipe implements PipeTransform {
transform(value: any, args: any[]): any {
return value.replace(/ /g, '');
}
}複製代碼
在Module文件中聲明這個Pipe:declarations: [PlaygroundComponent, TrimSpacePipe]
以便於其餘控件可使用這個Pipe:
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { PlaygroundRoutingModule } from './playground-routing.module';
import { PlaygroundComponent } from './playground.component';
import { PlaygroundService } from './playground.service';
import { TrimSpacePipe } from './trim-space.pipe';
@NgModule({
imports: [
SharedModule,
PlaygroundRoutingModule
],
providers:[
PlaygroundService
],
declarations: [PlaygroundComponent, TrimSpacePipe]
})
export class PlaygroundModule { }複製代碼
而後在組件的模板文件中使用便可 {{ birthday | date:'medium' | trimSpace}}
:
<p> Without Pipe: Today is {{ birthday }} </p>
<p> With Pipe: Today is {{ birthday | date:"MM/dd/yy" }} </p>
<p>The time is {{ birthday | date:'shortTime' }}</p>
<p>The time is {{ birthday | date:'medium' | trimSpace}} with trim space pipe applied</p>
<p>The time is {{ birthday | date:'medium' | uppercase }}</p>複製代碼
打開瀏覽器看一下效果,咱們看到應用了trimSpace管道的日期的空格被移除了,如圖所示:
DatePipe和UpperCase Pipe咱們剛剛已經見識過了,如今咱們看一看內建的其餘Pipe。首先是用於數字格式化的DecimalPipe。DecimalPipe的參數是以 {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}
的表達式形式體現的。其中:
<p>pi (no formatting): {{pi}}</p>
<p>pi (.5-5): {{pi | number:'.5-5'}}</p>
<p>pi (2.10-10): {{pi | number:'2.10-10'}}</p>
<p>pi (.3-3): {{pi | number:'.3-3'}}</p>複製代碼
若是咱們在組件中定義 pi: number = 3.1415927;
的話,上面的數字會被格式化成下圖的樣子
顧名思義,這個Pipe是格式化貨幣的,這個Pipe的表達式形式是這樣的: currency[:currencyCode[:symbolDisplay[:digitInfo]]]
,也就是說在currency管道後用分號分隔不一樣的屬性設置:
<p>A in USD: {{a | currency:'USD':true}}</p>
<p>B in CNY: {{b | currency:'CNY':false:'4.2-2'}}</p>複製代碼
上面的代碼中 USD
或 CNY
表面貨幣代碼,true
或 false
代表是否使用該貨幣的默認符號,後面若是再有一個表達式就是規訂貨幣的位數限制。這個限制的具體規則和上面Decimal Pipe的相似,以下圖所示。
這個管道固然就是用來格式化百分數的,百分數的整數位和小數位的規則也和上面提到的Decimal Pipe和Currency Pipe一致。若是在組件中定義 myNum: number = 0.1415927
; 下面的代碼會輸出成下圖的樣子:
<p>myNum : {{myNum | percent}}</p>
<p>myNum (3.2-2) : {{myNum | percent:'3.2-2'}}</p>複製代碼
這個管道我的感受更適合在調試中使用,它能夠把任何對象格式化成JSON格式輸出。若是咱們在組件中定義了一個對象:
object: Object = {
foo: 'bar',
baz: 'qux',
nested: {
xyz: 3,
numbers: [1, 2, 3, 4, 5]
}
};複製代碼
那麼下面的模板會輸出下圖的樣子,在調試階段,這個特性很好幫助你輸出可讀性很強的對象格式。固然若是你使用了現代化的IDE,這麼使用的意義就不是很大了:
<div>
<p>Without JSON pipe:</p>
<pre>{{object}}</pre>
<p>With JSON pipe:</p>
<pre>{{object | json}}</pre>
</div>複製代碼
另外一個咱們一直沒有提到的重要概念就是指令了,但這個雖然咱們沒提到,卻已經用過了。好比 *ngFor
,*ngIf
等,這些都叫作結構性指令,而像 *ngModel
等屬於屬性型指令。
Angular 2中的指令分紅三種:結構型(Structural)指令和屬性型(Attribute)指令,還有一種是什麼呢?就是Component,組件自己就是一個帶模板的指令。
結構型指令能夠經過添加、刪除DOM元素來更改DOM樹的佈局,好比咱們前面使用 *ngFor
在todo-list的模板中添加了多個todo-item。而屬性型指令能夠改變一個DOM元素的外觀或行爲,好比咱們利用 *ngModel
進行雙向綁定,改變了該組件的默認行爲(咱們在組件中改變某個變量值,這種改變會直接反應到組件上,這並非組件自身定義的行爲,而是咱們經過 *ngModel
來改變的)。
Angular 2中給出的內建結構型指令以下表所示:
名稱 | 用法 | 說明 |
---|---|---|
ngIf | <div*ngIf="canShow"> |
基於canShow表達式的值移除或從新建立部分DOM樹。 |
ngFor | <li *ngFor="let todo of todos"> |
把li元素及其內容轉化成一個模板,並用它來爲列表中的每一個條目初始化視圖。 |
ngSwitch, ngSwitchCase, ngSwitchDefault | <div [ngSwitch]="someCondition"></div> |
基於someCondition的當前值,從內嵌模板中選取一個,有條件的切換div的內容。 |
自定義一個指令也很簡單,咱們動手作一個。這個指令很是簡單就是使任何控件加上這個指令後,其點擊動做都會在console中輸出 「I am clicked」。因爲咱們要監視其宿主的click事件,因此咱們引入了 HostListener,在onClick方法上用 @HostListen(‘click’)
,代表在檢測到宿主發生click事件時調用這個方法。
import {
Directive,
HostListener
} from '@angular/core';
@Directive({
selector: "[log-on-click]",
})
export class LogOnClickDirective {
constructor() {}
@HostListener('click')
onClick() { console.log('I am clicked!'); }
}複製代碼
在模板中簡單寫一句就能夠看效果了
<button log-on-click>Click Me</button>
紙書出版了,比網上內容豐富充實了,歡迎你們訂購!
京東連接:item.m.jd.com/product/120…