起源
在各家框架中,使用程式來增減目標元素的 class 和 style 的內容,這是非常常用的技巧,在 Angular 也不例外,所以,這一篇特此紀錄一些 Angular 增減目標元素 class 和 style 的方法。
本文目錄
這系列的文章會記錄以下的內容
- 如何動態 binding 目標元素的 class
- 如何動態 binding 目標元素的 style
- 在 Angular 中判定目標元素應該以哪一個 style 為準的優先序
學習點
首先,先記錄為目標元素動態增減 class 的方法。
綁定單一個 css class
直接上一段程式碼<div [class.sale]="onSale">...</div>
上面這一段程式碼的效果為,
當 onSale 的值為 true
的時候,會為這個 div 元素的 class 加入 sale 這個 className,
反之,當 onSale 為 false
則會把 class 裡的 sale 刪掉。
綁定多個 css class
直接上一段程式碼<div [class]="classExpression"></div>
這個 classExpression
可以是以下幾種格式的其中一種:
- 一段字串,並將想要加入的 class 之間以空白格隔開
範例程式碼1
2
3
4
5
6
7
8
9
10--- app.component.ts ---
@Component({
selector: 'my-app',
template: `
<div [class]="extraClass">...</div>
`
})
export class AppComponent {
extraClass = 'modal success'; // 這是一段字串,將想加入的 class Name 之間以空白格隔開
}
最終結果會長這樣
- 傳入一個物件,其成員屬性為想加入的 class - 注意傳址的問題
這個要傳入的物件所含有的成員屬性名稱,就是要被加入到目標元素的 class 內的 class 名稱,
而這些成員屬性會接 truthy 或 falsy 的值,已決定是否該 className 會被加入到目標元素的 class 中。
範例程式碼
1 | --- app.component.ts --- |
上面這個範例會出現錯誤!!!
你會發現,當你按下按鈕之後,p
段落的 success 這個 className 並沒有隨著你按下按鈕,而被增減。
這是因為你是傳入物件進去,當你按下按鈕,只是單純地去修改該物件裡的屬性值,該物件的位址並沒有不同,所以, Angular 是不會偵測到裡面的值有更動過的,所以,才會造成以上的問題。
所以,要修改一下上面的範例程式碼內容
1 | changeClass(): void { |
我們重新創一個物件,並設給 extraClass,如此一來,就可以成功達成增減 success 這個 class 囉。
傳入一個陣列
這個方法就是傳入一個陣列,裡面的元素都是字串,它們的值就是要加入目標元素的 class。1
2
3
4
5
6
7
8
9
10
11
12
13
14@Component({
selector: 'app-root',
template: `
<p [class]="extraClass>Test Class</p>
<button type="button" (click)="spliceArr()>Change</button>
`
})
export class AppComponent {
addClassArr = ['modal', 'success'];
spliceArr(): void {
this.addClassArr = this.addClassArr.slice(1);
}
}要注意的是,如果,你想要刪減目標元素的 class 的話,
一樣是要將增減後的全新的陣列設給原本的陣列,
不然,又會出現傳址機制,導致沒有被偵測到有改變的問題。ternary operators (三元運算式)
可以加入 三元運算式 來決定是哪一種 class1
<div [class]="activated ? 'success' : 'error'">ternary operators</div>
當 activated 這個變數為
true
時,為該元素的 class 加入 success,反之,加入 error。
Appendix
Q1. [ngClass]
和 [class]
的差異? - 會把 host element 相同 className 刪掉的差異?
Ans:
在 Angular 裡面,我們也會用 ngClass
來做到動態增減目標元素的 class 內容的效果。
但它和 [class]
方法差在,[class]
會在一開始就先把相同的 className 拔掉。
什麼意思呢? 看一下下面的範例
1 | --- child.component.ts --- |
上面這段程式碼,可以看到 [class.modal]="false"
是代表不為目標元素加入 modal 這個 class。
另外,範例中的 app-child 會因為 @HostBinding('class.modal') modal = true
,而有一個既有的 modal 在它的 class 中了,
但它會因為 [class.modal]="false"
的效果,而導致一開始存在在 host element 中的 modal 這個 class 被刪掉。
這就是上面所說的 [class]
的寫法,會在一開始就先把既有的相同 className 的內容移除的的意思。
那來看一下, [ngClass] 差在哪
1 | --- app.component.ts --- |
改寫成用 ngClass 的方式,就算它這邊是判定不要加入 modal 這個 class,但是,它也不會一開始就先把 app-child 的 host element 的 class 中,既有的 modal 給刪掉。
但是,後續如果你操縱 isActive 這個變數,當它先從原始值的 false 換成 true,再換成 false 的時候,這個時候,在 host element 裡的 modal class 還是會被移除掉喔。
所以,可以看到 [ngClass]
和 [class]
的差異是在一開始是否會為指定元素的 class 內,相同的 className 移除與否的差別。
那為什麼要知道這個東西的差別,有時候,我們可能會引入外部元件或第三方套件具有一些很常用的 class,那我們就很有可能使用 [class] 綁定在該指定元素上,而造成 class 被移除掉,進而造成該元件原本設計的 css style 不見了的問題。
解決的辦法的話,除了小心命名 className 之外,或者就是直接使用 ngClass 來避免這種狀況囉。
Conclusion
上面記錄了四種 Angular 可以用來變換指定元素地 class 內容的方法,
要特別注意的是,如果,是傳入物件或陣列的話,會有傳址的問題需要特別處理一下。