手把手教你用ngrx管理Angular狀態(上)
前言
這是一篇字數超過公眾號編輯器限制的文章。本文由前端早讀課專欄作者@野草翻譯分享。
正文從這開始~
本文將與你一起探討如何用不可變數據儲存的方式進行Angular應用的狀態管理 :ngrx/store——Angular的響應式Redux。本文將會完成一個小型簡單的Angular應用,最終代碼可以在這裡下載。
Angular應用中的狀態管理
近幾年,大型複雜Angular/AngularJS項目的狀態管理一直是個讓人頭疼的問題。在AngularJS(1.x版本)中,狀態管理通常由服務,事件,$rootScope混合處理。在Angular中(2+版本),組件通信讓狀態管理變得清晰一些,但還是有點複雜,根據數據流向不同會用到很多方法。
注意:本文中,AngularJS特指1.x版本,Angular對應2.0版本及其以上。
有人用Redux來管理AngularJS或者Angular的狀態。Redux是JavaScript應用的可預測狀態容器,支持單一不可變數據儲存。Redux最有名的就是結合React的使用,當然它可以用於任意的視圖層框架。Egghead.io發布了一份非常優質的Redux免費視頻教程,視頻由Redux作者Dan Abramov本人講解。
初識ngrx/store
本文將採用ngrx/store管理我們的Angular應用。那麼,ngrx/store和Redux什麼關係呢?為什麼不用Redux呢?
與Redux的關係
ngrx/store的靈感來源於Redux,是一款集成RxJS的Angular狀態管理庫,由Angular的佈道者Rob Wormald開發。它和Redux的核心思想相同,但使用RxJS實現觀察者模式。它遵循Redux核心原則,但專門為Angular而設計。
ngrx/store中的基本原則
State(狀態) 是指單一不可變數據
Action(行為) 描述狀態的變化
Reducer(歸約器/歸約函數) 根據先前狀態以及當前行為來計算出新的狀態
狀態用State的可觀察對象,Action的觀察者——Store來訪問
我們會詳細解釋說明。先快速過一遍基礎,然後在實戰的過程中慢慢深入解釋。
Actions(行為)
Actions是信息的載體,它發送數據到reducer,然後reducer更新store。Actions是store能接受數據的唯一方式。
在ngrx/store里,Action的介面是這樣的:
// actions包括行為類型和對應的數據載體
exportinterfaceAction{
type:string;
payload?:any;
}
type描述我們期待的狀態變化類型。比如,添加待辦 ADD_TODO ,增加 DECREMENT 等。payload是發送到待更新store中的數據。store派發action的代碼類似如下:
// 派發action,從而更新store
store.dispatch({
type: ADD_TODO ,
payload: Buy milk
});
Reducers(歸約器)
Reducers規定了行為對應的具體狀態變化。它是純函數,通過接收前一個狀態和派發行為返回新對象作為下一個狀態的方式來改變狀態,新對象通常用Object.assign和擴展語法來實現。
// reducer定義了action被派發時state的具體改變方式
exportconsttodoReducer=(state=[],action)=>{
switch(action.type){
case ADD_TODO :
return[...state,action.payload];
default:
returnstate;
}
}
開發時特別要注意函數的純性。因為純函數:
不會改變它作用域外的狀態
輸出只決定於輸入
相同輸入,總是得到相同輸出
關於函數的純性,可以點擊這裡進一步了解。開發時,要確保函數的純性和狀態不可變性,所以寫reducers的時候要多加小心。
Store(存儲)
store中儲存了應用中所有的不可變狀態。ngrx/store中的store是RxJS狀態的可觀察對象,以及行為的觀察者。
我們可以利用Store來派發行為。當然,我們也可以用Store的select()方法獲取可觀察對象,然後訂閱觀察,在狀態變化之後做出反應。
ngrx/store實戰:個性寵物標籤
目前我們熟悉了ngrx/store的基本工作原理,接下來我們來開發一個能讓用戶自定義寵物名稱標籤的應用。該應用將會有以下功能:
用戶可以選擇標籤形狀,字體,文案,以及附加特性
創建過程可以預覽標籤效果
完成後,可以繼續創建
我們需要創建幾個組件來組合成標籤生成器和標籤預覽,還會添加登錄,創建標籤,完成創建的組件和路由。這個小應用的狀態將會用ngrx/store來管理。
完成後的個性寵物標籤app效果如下:
讓我們開始吧!
Angular應用設置
安裝依賴
確保你已經安裝了NodeJS,推薦LTS版本。
用npm安裝Angular CLI包,方便一鍵生成項目手腳架。運行以下命令來全局安裝angular-cli。
$ npm install-g @angular/cli
創建項目
選好項目所在的文件夾,打開命令行,輸入以下命令來創建一個新的Angular 項目:
$ ngnewpet-tags-ngrx
進入新創建的文件夾,安裝必要的包:
$ cd pet-tags-ngrx
$ npm install @ngrx/core @ngrx/store--save
一切準備就緒,可以開始開發了。
定製你的項目模板
讓我們根據這個項目的需求,稍微改造一下項目模板。
創建src/app/core文件夾
首先,創建文件夾src/app/core。應用的根組件和核心文件都會放在這個文件夾下。將所有的app.component.*文件移動到這裡。
注意:簡潔起見,該教程不會包含測試。我們會忽略所有的*.spec.ts文件。如果你想寫測試的話,可以自己寫。所以,文中不會再提到這些文件。同樣,出於清晰簡潔性的考慮,github倉庫的最終版代碼刪除了所有測試相關的文件。
更新App模塊
接著,打開src/app/app.module.ts文件,更新app.component 的路徑:
// src/app/app.module.ts
...
import{AppComponent}from ./core/app.component ;
...
靜態資源整理
定位到src/assets文件夾。
在assets文件夾下新建一個images的文件夾,稍後我們會添加一些圖片。然後,將根目錄下的src/styles.css移動到src/assets下。
styles.css的移動需要我們修改.angular-cli.json的配置。打開這個文件,把styles屬性改成如下:
// .angular-cli.json
...
"styles":[
"assets/styles.css"
],
...
集成Bootstrap
最後,在index.html中添加Bootstrap樣式。在標籤上加上CDN地址。這裡我們只用到樣式,不需要腳本文件。順便,更新一下標題,變成Custom Pet Tags:
...
Custom Pet Tags
...
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"
integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ"
crossorigin="anonymous">
啟動服務
我們可以在本地起個服務,然後監聽文件變化實時更新:
$ ng serve
在瀏覽器中輸入http://localhost:4200,程序成功運行。
App組件
現在開始創建新功能。從根組件app.component.*入手。不要擔心,變化很小。
刪除樣式文件
刪除app.component.css文件。該組件只用Bootstrap來定義樣式,所以不需要額外樣式。
根組件腳本
在app.component.ts文件中刪除對上述樣式文件的引用。我們也可以刪除AppComponent類中的title 屬性。
// src/app/core/app.component.ts
import{Component}from @angular/core ;
@Component({
selector: app-root ,
templateUrl: ./app.component.html
})
exportclassAppComponent{
}
根組件模版
在app.component.html中添加一些內容,變成如下:
Custom Pet Tags
我們用Bootstrap來添加珊格系統和標題。然後添加一個指令,這是當這個單頁面應用中添加路由後,視圖會渲染的地方。到現在為止,程序會報錯。等我們建好了路由和page組件的時候,就好了。
創建頁面組件
如上所述,應用會包含三個路由:登錄主頁,創建預覽頁,以及完成頁。
我們先創建好各頁面手腳架,以便搭建路由。然後再回來完善各個組件。
在根目錄下運行如下指令創建頁面組件:
$ ng g component pages/home
$ ng g component pages/create
$ ng g component pages/complete
ng g命令可以快速生成組件,指令,過濾器和服務,同時也會自動把生成的文件導入到app.module.ts中。現在,我們有三個頁面組件的腳手架,可以開始搭建路由了。
搭建路由
新建一個路由模塊,在src/app/core文件夾下創建一個app-routing.module.ts文件:
// src/app/core/routing-module.ts
import{NgModule}from @angular/core ;
import{RouterModule}from @angular/router ;
import{HomeComponent}from ../pages/home/home.component ;
import{CreateComponent}from ./../pages/create/create.component ;
import{CompleteComponent}from ./../pages/complete/complete.component ;
@NgModule({
imports:[
RouterModule.forRoot([
{
path: ,
component:HomeComponent
},
{
path: create ,
component:CreateComponent
},
{
path: complete ,
component:CompleteComponent
},
{
path: ** ,
redirectTo: ,
pathMatch: full
}
])
],
providers:[],
exports:[
RouterModule
]
})
exportclassAppRoutingModule{}
現在有三個路由/,/create,/complete,未知路由會重定向到首頁。
打開根模塊文件app.module.ts,添加新增路由模塊AppRoutingModule至imports 屬性。
// src/app/app.module.ts
...
import{AppRoutingModule}from ./core/app-routing.module ;
@NgModule({
...,
imports:[
...,
AppRoutingModule
],
...
到此,路由設置完畢。我們可以通過路由來訪問不同頁面,訪問首頁的時候,HomeComponent就會渲染在所在的位置,如下圖所示:
「Home」頁面組件
HomeComponent會有簡單的信息提示,以及登錄按鈕。點擊登錄按鈕,直接跳轉到/create頁面。
Please sign up or loginto create a custom name tagforyour beloved pet!
class="btn btn-lg btn-primary"
routerLink="/create">Log In
現在,首頁效果如下:
寵物標籤模型
開始實現個性寵物標籤生成器功能和狀態管理的工作了。首先,為我們的狀態創建一個數據模型,該模型描述了當前的寵物標籤。
新建文件src/app/core/pet-tag.model.ts:
// src/app/core/pet-tag.model.ts
exportclassPetTag{
constructor(
publicshape:string,
publicfont:string,
publictext:string,
publicclip:boolean,
publicgems:boolean,
publiccomplete:boolean
){}
}
exportconstinitialTag:PetTag={
shape: ,
font: sans-serif ,
text: ,
clip:false,
gems:false,
complete:false
};
PetTag類聲明了寵物標籤的屬性和類型,接著我們定義一個常量initialTag作為默認初始值,在初始化和重置狀態時需要用到。
寵物標籤行為
現在可以創建行為類型了。回顧之前說的,action被派發到reducer中,從而更新store。現在為我們想要的每種行為定義名字。
創建文件src/app/core/pet-tag.actions.ts
// src/app/core/pet-tag.actions.ts
exportconstSELECT_SHAPE= SELECT_SHAPE ;
exportconstSELECT_FONT= SELECT_FONT ;
exportconstADD_TEXT= ADD_TEXT ;
exportconstTOGGLE_CLIP= TOGGLE_CLIP ;
exportconstTOGGLE_GEMS= TOGGLE_GEMS ;
exportconstCOMPLETE= COMPLETE ;
exportconstRESET= RESET ;
將行為定義為常量。我們也可以構造可注入的行為類,就像ngrx/example-app中那樣。但我們這個例子很簡單,用這種方法反而會增加複雜度。
寵物標籤歸約器
現在可以創建我們的歸約函數了,這個函數接受action,更新store。
新建文件src/app/core/pet-tag.reducer.ts:
// src/app/core/pet-tag.reducer.ts
import{Action}from @ngrx/store ;
import{PetTag,initialTag}from ./../core/pet-tag.model ;
import{SELECT_SHAPE,SELECT_FONT,ADD_TEXT,TOGGLE_CLIP,TOGGLE_GEMS,COMPLETE,RESET}from ./pet-tag.actions ;
exportfunctionpetTagReducer(state:PetTag=initialTag,action:Action){
switch(action.type){
caseSELECT_SHAPE:
returnObject.assign({},state,{
shape:action.payload
});
caseSELECT_FONT:
returnObject.assign({},state,{
font:action.payload
});
caseADD_TEXT:
returnObject.assign({},state,{
text:action.payload
});
caseTOGGLE_CLIP:
returnObject.assign({},state,{
clip:!state.clip
});
caseTOGGLE_GEMS:
returnObject.assign({},state,{
gems:!state.gems
});
caseCOMPLETE:
returnObject.assign({},state,{
complete:action.payload
});
caseRESET:
returnObject.assign({},state,initialTag);
default:
returnstate;
}
}
首先從ngrx/store導入Action。同時也需要PetTag數據模型以及它的初始狀態initialTag。還有上一步中創建的行為類型也需要導入。
然後創建petTagReducer()函數,該函數接收兩個參數:上一個狀態state和被派發的行為action。注意它是輸入決定輸出的純函數,函數不會改變全局的狀態。這就是說,從歸約器返回的數據要麼是新對象,要麼是未修改的輸入,比如default情況。
通常,我們可以借用Object.assign()從輸入數據中得到全新的對象。輸入數據是上一個狀態以及包含行為載體(payload)的對象。
TOGGLE_CLIP和TOGGLE_GEMS切換initialTag狀態中的布爾值,所以當我們派發這兩種行為的時候,不需要行為載體,我們只需要簡單取反即可。
COMPLETE行為需要一個載體,因為我們明確要將其設置為true,而且每個標籤只能操作一次。我們也可以切換布爾值,但明確起見,我們還是會派發一個具體的值作為行為載體。
注意:注意RESET行為用到導入的initialTag。因為它是個不變數,所以在這裡使用並不會違背歸約函數的純性。
根模塊導入Store
完成了行為和歸約函數的定義之後,我們要告訴應用程序有這些的存在。打開app.module.ts文件,更新如下:
// src/app/app.module.ts
...
import{StoreModule}from @ngrx/store ;
import{petTagReducer}from ./core/pet-tag.reducer ;
@NgModule({
...,
imports:[
...,
StoreModule.provideStore({petTag:petTagReducer})
],
...
現在,我們可以用Store來實現狀態管理了。
創建「Create」頁面
之前創建的CreateComponent是個智能組件(Smart Component),它會有幾個木偶子組件(Dumb Component)。
智能組件/木偶組件
智能組件也稱容器組件,通常作為根級組件,包含業務邏輯,狀態管理,訂閱,處理事件。在這個例子中,就是那些可路由的頁面組件。CreateComponent是智能組件,它將為標籤生成器制定業務邏輯。同時,它會處理木偶子組件觸發的事件,而這些子組件是標籤生成器的一部分。
木偶組件又名展示組件,它只決定於父組件傳遞的數據。它可以觸發事件,然後在父組件中處理,但它不會直接影響訂閱或者store。木偶組件是可復用的模塊化組件。比如,我們會同時在Create頁面和Complete頁面使用標籤預覽這個木偶組件(CreateComponent和CompleteComponent是智能組件)。
「Create」頁面功能點
Create頁面將會有以下幾個功能:
標籤形狀選擇
標籤字體選擇和文案輸入
是否添加clip和gems
標籤形狀和文案的預覽
結束操作的完成按鈕
「Create」組件腳本
// src/app/pages/create/create.component.ts
import{Component,OnInit,OnDestroy}from @angular/core ;
import{Observable}from rxjs/Observable ;
import{Subscription}from rxjs/Subscription ;
import{Store}from @ngrx/store ;
import{SELECT_SHAPE,SELECT_FONT,ADD_TEXT,TOGGLE_CLIP,TOGGLE_GEMS,COMPLETE}from ./../../core/pet-tag.actions ;
import{PetTag}from ./../../core/pet-tag.model ;
@Component({
selector: app-create ,
templateUrl: ./create.component.html
})
exportclassCreateComponentimplementsOnInit,OnDestroy{
tagState$:Observable;
privatetagStateSubscription:Subscription;
petTag:PetTag;
done=false;
constructor(privatestore:Store){
this.tagState$=store.select( petTag );
}
ngOnInit(){
this.tagStateSubscription=this.tagState$.subscribe((state)=>{
this.petTag=state;
this.done=!!(this.petTag.shape&&this.petTag.text);
});
}
ngOnDestroy(){
this.tagStateSubscription.unsubscribe();
}
selectShapeHandler(shape:string){
this.store.dispatch({
type:SELECT_SHAPE,
payload:shape
});
}
selectFontHandler(fontType:string){
this.store.dispatch({
type:SELECT_FONT,
payload:fontType
});
}
addTextHandler(text:string){
this.store.dispatch({
type:ADD_TEXT,
payload:text
});
}
toggleClipHandler(){
this.store.dispatch({
type:TOGGLE_CLIP
});
}
toggleGemsHandler(){
this.store.dispatch({
type:TOGGLE_GEMS
});
}
submit(){
this.store.dispatch({
type:COMPLETE,
payload:true
});
}
}
這個智能組件主要作用為自定義寵物標籤。
引入OnInit以及OnDestroy,分別用於初始化和銷毀訂閱。同時,需要從RxJS中引入Observable和Subscription,從ngrx/store引入Store對象。由於行為基本都在這個組件中派發,所以需要引入之前定義好的所有行為(除RESET外)。最後,引入PetTag數據模型。
該組件不需要額外的樣式,所以刪除CSS文件以及對它的引用。
該類中,tagState$定義為PetTag數據類型的可觀察對象,通過構造器中用store的select()方法賦值實現。
在ngOnInit()鉤子函數中,將subscription(訂閱)設置為對tagState$可觀察對象的訂閱。每當有新狀態生成時,訂閱就會把petTag設置為可觀察對象流返回的新狀態state。done屬性用來檢查shape和text是否已經填寫。這兩個屬性是標籤完成的必填項。當組件銷毀的時候,ngOnDestroy()鉤子函數被觸發,執行銷毀訂閱。
最後,創建派發行為至store的事件處理函數。當子木偶組件觸發事件來更新狀態時,這些事件處理函數就會執行。每個函數都用store.dispatch()派發期望的行為類型type和行為載體payload至歸約函數。
注意: 在更複雜的應用中,你可能希望在單獨的服務中派發行為,然後注入到組件中。不過,現在我們這個僅僅為學習而創建的小應用,沒有必要這麼做。直接在智能組價中派發行為就行了。
形狀組件
開始創建我們的第一個展示組件:TagShapeComponent。當該組件完成時,創建頁面預期效果如下:
用Angular CLI命令一鍵生成這個子組件的腳手架:
$ ng g component pages/create/tag-shape
標籤形狀組件將會展示四種不同的形狀圖片:骨頭形,方形,圓形,心形。用戶可以從中選擇喜歡的形狀。
從git倉庫下載圖片,放置在pet-tags-ngrx/src/assets/images目錄下。
形狀組件腳本
// src/app/pages/create/tag-shape/tag-shape.component.ts
import{Component,Output,EventEmitter}from @angular/core ;
@Component({
selector: app-tag-shape ,
templateUrl: ./tag-shape.component.html ,
styleUrls:[ ./tag-shape.component.css ]
})
exportclassTagShapeComponent{
tagShape:string;
@Output()selectShapeEvent=newEventEmitter();
constructor(){}
selectShape(shape:string){
this.selectShapeEvent.emit(shape);
}
}
從@angular/core引入Output和EventEmitter。
形狀選擇用radio按鈕表示,所以需要一個屬性來儲存形狀。由於形狀是用字元串來描述的,我們將tagShape的類型設置為string。
當用戶選擇某個形狀之後,我們需要裝飾器@Output來觸發事件。並且發送信息至父組件CreateComponent。selectShape(shape)函數會觸發攜帶形狀信息的事件,然後父組件用先前在CreateComponent定義的selectShapeHandler()去處理。稍後我們就可以看到父子組件共同工作的效果。
形狀組件模版
Shape
Choose a tag shape togetstarted!
type="radio"
name="shape"
[(ngModel)]="tagShape"
(change)="selectShape(tagShape)"
value="bone">
type="radio"
name="shape"
[(ngModel)]="tagShape"
(change)="selectShape(tagShape)"
value="rectangle">
type="radio"
name="shape"
[(ngModel)]="tagShape"
(change)="selectShape(tagShape)"
value="circle">
type="radio"
name="shape"
[(ngModel)]="tagShape"
(change)="selectShape(tagShape)"
value="heart">
創建四個radio按鈕分別對應四個形狀的圖片。無論選擇哪個,都會觸發(change)事件,然後觸發攜帶tagShape參數的selectShapeEvent事件。
形狀組件樣式
/* src/app/pages/create/tag-shape/tag-shape.component.css */
:host{
display:block;
margin:20px;
}
.tagShape{
padding:10px;
text-align:center;
}
img{
display:block;
height:auto;
margin:auto;
max-height:50px;
max-width:100%;
width:auto;
}
注意::host偽類選擇器是用來獲取組件的宿主元素,也就是。
添加形狀組件至Create頁面
Hello!Create a customized tagforyour pet.
(selectShapeEvent)="selectShapeHandler($event)">
父組件現在能監聽到來自子組件的selectShapeEvent事件,同時通過執行之前在CreateComponent中定義的selectShapeHandler()函數來處理該事件。回憶一下,這個函數派發了SELECT_SHAPE行為至store:
selectShapeHandler(shape:string){
this.store.dispatch({
type:SELECT_SHAPE,
payload:shape
});
}
現在,應用可以在用戶選擇形狀的時候更新狀態了。
文字組件
現在我們來創建用戶輸入字體和文字的組件。完成後,頁面期待效果如下:
用命令行來創建該組件的手腳架:
$ ng g component pages/create/tag-text
文字組件腳本
// src/app/pages/create/tag-text/tag-text.component.ts
import{Component,Output,EventEmitter}from @angular/core ;
@Component({
selector: app-tag-text ,
templateUrl: ./tag-text.component.html ,
styleUrls:[ ./tag-text.component.css ]
})
exportclassTagTextComponent{
tagTextInput= ;
fontType= sans-serif ;
@Output()selectFontEvent=newEventEmitter;
@Output()addTextEvent=newEventEmitter;
constructor(){}
selectFont(fontType:string){
this.selectFontEvent.emit(fontType);
}
addText(text:string){
this.addTextEvent.emit(text);
}
}
該組件跟上一個組件TagShapeComponent工作方式相同,所以代碼也差不多。引入Output和EventEmitter,並且創建tagTextInput和fontType屬性來記錄用戶的輸入。
注意:這裡我們不需要聲明string類型,因為初始值默認定義了變數的類型。
當用戶修改字體或者文字時,組件就會觸發事件讓父組件捕獲。
文字組件模板
Text
Select your desired font style and enter your pet s name.
You can see what your tag will look likeinthe preview below.
Font:
id="font"
name="font"
class="form-control col-sm-6"
[(ngModel)]="fontType"
(change)="selectFont(fontType)">
Sans-serif
Serif
Text:
id="tagText"
type="text"
class="form-control col-sm-6"
[(ngModel)]="tagTextInput"
(input)="addText(tagTextInput)"
maxlength="8"/>
我們用元素和文本輸入框讓用戶操作選擇自己喜歡的效果。用戶輸入時,ngModel保證了數據的雙向一致性,(change)使得該組件觸發事件到父組件。
文字組件樣式
/* src/app/pages/create/tag-text/tag-text.component.css */
:host{
display:block;
margin:20px;
}
添加文字組件至Create頁面
最後將TagTextComponent組件添加到Create 頁面:
...
*ngIf="petTag.shape"
(selectFontEvent)="selectFontHandler($event)"
(addTextEvent)="addTextHandler($event)">
注意我們在元素上加了*ngIf結構指令,我們希望用戶在選擇了標籤形狀之後才顯示該組件。我們即將創建標籤預覽組件,沒有標籤形狀的預覽沒有什麼意義,*ngIf保證了這點。
在父組件中監聽TagTextComponent的selectFontEvent和addTextEvent事件,然後用之前在CreateComponent定義好的方法去處理。處理方式分別為派發SELECT_FONT和ADD_TEXT以及對應的行為載體(payload)至歸約器(reducer):
selectFontHandler(fontType:string){
this.store.dispatch({
type:SELECT_FONT,
payload:fontType
});
}
addTextHandler(text:string){
this.store.dispatch({
type:ADD_TEXT,
payload:text
});
}
附加特性組件
現在,我們來添加能讓用戶額外選擇的幾個特性。完成之後,創建頁面預期效果如下:
同樣用命令來創建TagExtrasComponent組件的手腳架:
$ ng g component pages/create/tag-extras
附加特性組件腳本
// src/app/pages/create/tag-extras/tag-extras.component.ts
import{Component,Output,EventEmitter}from @angular/core ;
@Component({
selector: app-tag-extras ,
templateUrl: ./tag-extras.component.html ,
styleUrls:[ ./tag-extras.component.css ]
})
exportclassTagExtrasComponent{
tagClip:boolean;
gems:boolean;
@Output()toggleClipEvent=newEventEmitter;
@Output()toggleGemsEvent=newEventEmitter;
constructor(){}
toggleClip(){
this.toggleClipEvent.emit();
}
toggleGems(){
this.toggleGemsEvent.emit();
}
}
代碼看起來應該很熟悉了吧?! 額外選項包括是否添加clip和gems,所以這兩個參數是boolean類型。
附加特性組件模板
Extras
Select any extras you would like to add.
type="checkbox"
[(ngModel)]="tagClip"
(change)="toggleClip()">Include tag clip
type="checkbox"
[(ngModel)]="gems"
(change)="toggleGems()">Add gems
用checkbox讓用戶來勾選是否要添加額外特性。
附加特性組件樣式
/* src/app/pages/create/tag-extras/tag-extras.component.css */
:host{
border-bottom:1px solid #ccc;
display:block;
margin:20px;
padding-bottom:20px;
}
添加附加特性組件至Create頁面
...
*ngIf="petTag.shape"
(toggleClipEvent)="toggleClipHandler()"
(toggleGemsEvent)="toggleGemsHandler()">
跟標籤文字組件一樣,我們只在用戶選擇了標籤形狀之後才顯示附加特性選項。toggleClipEvent和toggleGemsEvent事件被之前在CreateComponent定義好的方法處理,處理方式分別為派發TOGGLE_CLIP和TOGGLE_GEMS行為至歸約器:
toggleClipHandler(){
this.store.dispatch({
type:TOGGLE_CLIP
});
}
toggleGemsHandler(){
this.store.dispatch({
type:TOGGLE_GEMS
});
}
由於選擇的切換是布爾值,不需要行為載體。這種情況,我們只需要在歸約器中使用上一個狀態來決定下一個狀態。
哎啊,超出了自述,關於組件預覽部分請大家看第二條
點擊展開全文
※「大產品小細節」Hick『s Law 希克法則
※認識 V8 引擎
※我接觸過的前端數據結構與演算法
※阿里巴巴大優酷事業部前端專家
※團隊技術信息流建設
TAG:前端早讀課 |
※TelephonyManager(電話管理器)
※Spring Boot 基礎教程 ( 三 ) :使用 Cloud Studio 在線編寫、管理 Spring Boot 應用
※AudioManager(音頻管理器)
※linux 使用supervisor管理開機啟動uwsgi
※監控管理之Spring Boot Admin使用
※管理vRealize Automation的vPostgres資料庫
※在docker for win中使用portainer管理容器
※如何用 Google Tag Manager標籤管理器設置GA onclick按鈕點擊事件
※《自我管理:Managing Oneself》導讀
※管理非凡的心智 Managing Brilliant Minds
※把玩Alpine linux(二):APK包管理器
※Windows 上的 SSH?使用 PowerShell Remoting 遠程管理 Windows 伺服器
※在 Linux 上使用 Lutries 管理你的遊戲
※管理髮展 management development
※Veritas收購雲數據管理公司fluid Operations AG
※Pure Storage收購瑞典文件管理軟體公司Cpmpuverde
※Kube:在VS Code中管理Helm charts
※Mozilla推出兼容iOS的Face ID密碼管理器Firefox Lockbox
※使用 Stratis 從命令行管理 Linux 存儲
※react+react-router+mobx+element打造管理後台系統