目前為止我們完全沒有針對傳入 @Input 屬性的資料作任何的消毒(Sanitization),那麼實際上該怎麼做呢?
Getter/Setter(484 很眼熟)
我們可以攔截要消毒的欄位
攔截指的是把 @Input 的 binding 對應到一個 setter 方法, 其實作是將真正的值 存放在一個 private property, 換言之,也是封裝的一種
另一方面 getter 方法, 回傳的也是上方提及的 private property 的值
事不宜遲,趕快來改造一下 MetricComponent 的 Controller:
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-metric',
templateUrl: './metric.component.html',
styleUrls: ['./metric.component.css']
})
export class MetricComponent {
private _value: number = 0;
private _max: number = 100;
@Input() title: string = '';
@Input() description: string = '';
@Input('used')
set value(value: number) {
if(isNaN(value)) value = 0;
this._value = value;
}
get value() { return this._value; }
@Input('available')
set max(max: number) {
if(isNaN(max)) max = 100;
this._max = max;
}
get max(){ return this._max; }
isDanger() {
return this.value / this.max > 0.7;
}
}
Content Projection(※很重要)
假設你有一個 CardComponent,而且想讓它的內容是能海納百川,放入任何內容都行的話,該怎麼辦?
我們既將在這個 Dashboard 新增兩個 Component,分別是 Nodes 與 Nodes-Row
ng g c nodes
ng g c nodes-row
<thead>
<tr>
<th>Node</th>
<th [colSpan]="2">CPU</th>
<th [colSpan]="2">Memory</th>
<th>Details</th>
</tr>
</thead>
<ng-content></ng-content>
selector: 'app-nodes'
改成
selector: '[app-nodes]'
Nodes 已經完成了 table header 的部份,接著要處理 row 的部份
加入以下的內容到 nodes-row 的 template
<th scope="row">{{ node.name }}</th>
<td [class.table-danger]="isDanger('cpu')">
{{ node.cpu.used }}/{{ node.cpu.available }}
</td>
<td [class.table-danger]="isDanger('cpu')">
({{ node.cpu.used / node.cpu.available | percent }})
</td>
<td [class.table-danger]="isDanger('mem')">
{{ node.mem.used }}/{{ node.mem.available }}
</td>
<td [class.table-danger]="isDanger('mem')">
({{ node.mem.used / node.mem.available | percent }})
</td>
<td><button class="btn btn-secondary">View</button></td>
有了 Nodes 與 Nodes-row,接著就要塞資料了,資料從哪來? 當然是 DashboardComponent(資料型),所以要定義 nodes-row.component.ts 如下:
import { Component, Input } from '@angular/core';
@Component({
selector: '[app-nodes-row]',
templateUrl: './nodes-row.component.html',
styleUrls: ['./nodes-row.component.css']
})
export class NodesRowComponent {
constructor() { }
@Input() node: any;
isDanger(prop) {
return this.node[prop].used / this.node[prop].available > 0.7;
}
}
快要大功告成了,只差 Dashboard
<div class="container mt-2">
<div class="card card-block">
<div class="card-body">
<nav class="navbar navbar-dark bg-inverse mb-1">
<h1 class="navbar-brand mb-0">Cluster 1</h1>
</nav>
<table app-nodes class="table table-hover">
<tr app-nodes-row *ngFor="let node of cluster1" [node]="node"></tr>
</table>
<nav class="navbar navbar-dark bg-inverse mb-1">
<h1 class="navbar-brand mb-0">Cluster 2</h1>
</nav>
<table app-nodes class="table table-hover">
<tr app-nodes-row *ngFor="let node of cluster2" [node]="node"></tr>
</table>
</div>
</div>
</div>
如果我們需要一個以上的插入點呢?
<div class="card card-block">
<div class="card-body">
<nav
class="navbar navbar-dark bg-primary mb-1"
[ngClass]="{ 'bg-danger': isDanger(), 'bg-success': !isDanger() }"
>
<h1 class="navbar-brand mb-0">
<ng-content select="metric-title"></ng-content>
</h1>
</nav>
<h4 class="card-title">
{{ value }}/{{ max }} ({{ value / max | percent: "1.0-2" }})
</h4>
<p class="card-text">
<ng-content select="metric-description"></ng-content>>
</p>
<ngb-progressbar
[value]="value"
[max]="max"
[type]="isDanger() ? 'danger' : 'success'"
></ngb-progressbar>
</div>
</div>
// Remove these two declarations
@Input() title: string = '';
@Input() description: string = '';
...(略)
<app-metric
class="col-sm-6"
[used]="cpu.used"
[available]="cpu.available"
[title]="'CPU'"
[description]="'utilization of CPU cores'"
>
<metric-title>CPU</metric-title>
<metric-description>utilization of CPU cores</metric-description>
</app-metric>
<app-metric
class="col-sm-6"
[used]="mem.used"
[available]="mem.available"
[title]="'Memory'"
[description]="'utilization of memory in GB'"
>
<metric-title>Memory</metric-title>
<metric-description>utilization of memory in GB</metric-description>
</app-metric>
...(ellipsised)
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
// ...(ellipsised)
bootstrap: [AppComponent],
schemas: [NO_ERRORS_SCHEMA]
以上,就是一個真真假假,假假真真的 Dashboard