Angular實(shí)現(xiàn)雙向折疊列表組件的示例代碼
最近在做一個(gè)雙向折疊組件,如下圖所示,頁面是分為兩組,左邊頁面是Summary Panel,主要是一組列表,右邊頁面是Detail Panel,展示左邊列表中某一項(xiàng)的具體信息,我們把它記作“Middle State”。

我們還看到有“<”和“>”兩組按鈕,這就是我們要做的“雙向折疊組件”。點(diǎn)擊左邊的“<”,Summary Panel折疊起來,Detail Panel鋪滿整個(gè)頁面,我們把它記作“Left State”,如下圖:

在Summary Panel折疊狀態(tài)下,點(diǎn)擊“>”,又回到“Middle State”。點(diǎn)擊“>”,Detail Panel折疊起來,Summary Panel鋪滿整個(gè)頁面,我們把它記作“Right State”,如下圖:

我們通過以上的需求分析可知,
1.頁面的總體布局是一個(gè)Summary的div,兩個(gè)箭頭buttons,一個(gè)Detail的div。
2.頁面總共有三種state:“Middle”、“Left”、“Right”,有兩個(gè)button:“<”和“>”,也就是兩個(gè)button去控制三個(gè)state。
因此我們需要定義一個(gè)枚舉來記錄頁面的三種狀態(tài)(注意,定義枚舉要用export導(dǎo)出,否則后面會出錯(cuò))
export enum CollapseExpandState {
Middle = 1,
Left,
Right
}
頁面的結(jié)構(gòu)如下,并且通過一個(gè)變量_collapseExpandState去控制“l(fā)eft”和“right”兩個(gè)button,具體為“<”會在頁面狀態(tài)為“Middle”和“Right”的情況下出現(xiàn),“>”會在頁面狀態(tài)為“Middle”和“Left”狀態(tài)下出現(xiàn),從需求圖中即可得知:
<div id="container">
<div id="summary"></div>
<div id="buttons">
<div id="left" *ngIf="_collapseExpandState === CollapseExpandState.Middle || CollapseExpandState.Right" (click)="_onHandleLeft($event)">《</div>
<div id="right"> *ngIf="_collapseExpandState === CollapseExpandState.Middle || CollapseExpandState.Left" (click)="_onHandleLeft($event)">》</div>
</div>
<div id="detail"></div>
</div>
這里在angular的template中用到了枚舉,遭遇了一些麻煩,如果我們按上述定義了枚舉,并且在Angular Component的template中用了枚舉,我們會得到以下的錯(cuò)誤提示:
TypeError: Cannot read property 'Middle' of undefined
也就是說,在Angular2的template中無法識別定義的枚舉類型CollapseExpandState,這是因?yàn)槟銓懙腁ngular Component的template模板的執(zhí)行環(huán)境是你定義的component class,但是在class中并沒有關(guān)于CollapseExpandState枚舉的任何引用,所以Angular在為你的component生成模板的時(shí)候認(rèn)為CollapseExpandState是undefined的。知道了原因,解決方案就很容易了,只需要在component class中加入這個(gè)枚舉的引用即可:
@component(...)
export class ContainerWidget {
public CollpaseExpandState: any = CollapseExpandState;
}
我們通過枚舉狀態(tài)來控制了兩個(gè)buttons是否在恰當(dāng)?shù)捻撁鏍顟B(tài)顯示與否,但是即使是同一個(gè)buttons,在不同的頁面狀態(tài)下所用到的樣式也會不同,這里的樣式其實(shí)最主要的就是位置了。我們先來考慮如何去控制button的樣式,再來考慮如何去正確定位不同頁面狀態(tài)下button的位置。
對于控制button的樣式,我們需要控制三個(gè)樣式:"left button"、"right button"還有“buttons”。能夠想到有三種方案:
I、用ng-class
ng-class一般的用法如下:
<some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>
因此它需要用boolean去控制,每一種樣式需要一個(gè)boolean去控制,left和right各有兩種樣式,buttons有三種樣式,這樣就需要用5個(gè)boolean去控制,略顯麻煩。
II、 用ElementRef.nativeElement.className
分別在buttons、left和right上用模板變量,然后在class中定義:
@ViewChild("buttons") buttons: ElementRef;
@ViewChild("left") left: ElementRef;
@ViewChild("right") right: ElementRef;
在處理函數(shù)中這樣去給class賦值從而改變樣式:
this.left.nativeElement.className = "XXXXX";
這樣的話,我們需要從元素的角度出發(fā),只需要3個(gè)元素變量,從而改變元素上的className即可。但是這樣做有個(gè)隱患,注意到我們是用的ngIf來控制left和right在不同狀態(tài)下是否存在,因?yàn)槊恳淮问录幚矶夹枰獙θ齻€(gè)元素的樣式進(jìn)行賦值,但是如果某個(gè)頁面狀態(tài)下ngIf為false從而元素不存在,那么就會報(bào)“Null Pointer”的錯(cuò)誤,所以如果所引用的元素受到了ngIf的控制,不確定是否一定存在的情況下,要慎用該方法為元素賦予樣式。
III、 用class="{{}}"
為了II中的尷尬,我們采用在HTML元素上對class進(jìn)行直接賦值的方式,但是需要借用插值表達(dá)式{{}}。我們在css中用class的形式定義好樣式,并且在compoennt class中定義三個(gè)字符串變量記錄className,然后在事件處理函數(shù)中把相應(yīng)的className賦予變量即可。這樣我們就不用擔(dān)心元素是否存在而導(dǎo)致的空指針了。
考慮完如何控制樣式,下面我們進(jìn)入CSS樣式的討論,這里其實(shí)主要考慮的就是位置。
我們采用flex布局,從左到右依次排列Summary Panel, buttons和Detail Panel。我們希望Buttons向左移,但是空出的位置被Detail Panel來填充。首先來看一下不設(shè)樣式的效果圖:

顯然buttons是占據(jù)了文檔流的位置的,如果這時(shí)候我們用relative定位buttpms,并且設(shè)置left的值為-74px(注意到left為負(fù)數(shù)就會把元素往左推):
.buttons{
display: flex;
position: relative;
margin-top: 23px;
left: -74px;
}
效果圖為:

發(fā)現(xiàn)如果用left的話,buttons原來的文檔流位置依然存在,只是buttons相對于原來的位置移動了一定的位移。
如果我們用margin-left來設(shè)置呢:
.buttons{
display: flex;
position: relative;
margin-top: 23px;
margin-left: -74px;
}
效果圖為:

它和left不同之處在于,left會留住原來的文檔流位置,但是用margin-left原來的文檔流位置會消失,而由后面的元素補(bǔ)充過來,而我們想要的效果,正好是用margin-left來實(shí)現(xiàn)的。
所以用CSS定位的時(shí)候,要明白left和margin-left的區(qū)別,從而選擇正確的方式來定位。
總結(jié)一下,從這個(gè)案例中我們學(xué)習(xí)到了:
- 雙向折疊可以用“3種頁面狀態(tài)去控制2個(gè)按鈕”來實(shí)現(xiàn)
- enum在Angular Component的template中用到時(shí),需要在compnent class中添加它的引用
- 控制元素樣式有很多方法,要選擇合適的方法
- CSS定位中l(wèi)eft和margin-left雖然都能把元素推向左邊,但是left保留原來文檔流位置,margin-left不保留原來文檔流位置。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于AngularJs數(shù)據(jù)的本地存儲詳解
本文主要介紹了每一個(gè)獨(dú)立的JS文件或者不同的控制器如何實(shí)現(xiàn)數(shù)據(jù)的共享與交互的方法。具有一定的參考價(jià)值,下面跟著小編一起來看下吧2017-01-01
angularjs實(shí)現(xiàn)柱狀圖動態(tài)加載的示例
本篇文章主要介紹了angularjs實(shí)現(xiàn)柱狀圖動態(tài)加載的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12
Angular進(jìn)行簡單單元測試的實(shí)現(xiàn)方法實(shí)例
這篇文章主要給大家介紹了關(guān)于Angular進(jìn)行簡單單元測試的實(shí)現(xiàn)方法,文中僅用了幾行代碼,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Angular具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
Angular在模板驅(qū)動表單中自定義校驗(yàn)器的方法
本章介紹的是如何對模板驅(qū)動表單創(chuàng)建自定義校驗(yàn)器,它相比較響應(yīng)式表單自定義校驗(yàn)器略為復(fù)雜一些。接下來通過本文給大家分享Angular在模板驅(qū)動表單中自定義校驗(yàn)器的方法,感興趣的朋友一起看看吧2017-08-08
Angularjs自定義指令實(shí)現(xiàn)分頁插件(DEMO)
由于最近的一個(gè)項(xiàng)目使用的是angularjs1.0的版本,涉及到分頁查詢數(shù)據(jù)的功能,后來自己就用自定義指令實(shí)現(xiàn)了該功能,下面小編把實(shí)例demo分享到腳本之家平臺,需要的朋友參考下2017-09-09
angular route中使用resolve在uglify壓縮后問題解決
這篇文章主要介紹了angular route中使用resolve在uglify壓縮后問題解決的相關(guān)資料,需要的朋友可以參考下2016-09-09
淺談關(guān)于angularJs中使用$.ajax的注意點(diǎn)
本篇文章主要介紹了關(guān)于angularJs中使用$.ajax的注意點(diǎn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08
Angular.js中window.onload(),$(document).ready()的寫法淺析
這篇文章主要介紹了Angular.js中window.onload(),$(document).ready()的寫法淺析,需要的朋友可以參考下2017-09-09

