選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
5 分鐘閱讀時間 (982 個字)

Angular #22 - 深入 Components [5]

shutterstock_198004562
  • 動態渲染 Component

    • 看到動態我頭就疼(X),到底誰發明的?

    • 但有的時候你就是得面對人生,因為不知道何時何地,會出現一個情境需要動態長出 Component,以下是幾個範例:

      • Modals: 你一定看過,就是那種跳出來擋住後面不讓你操作的白目視窗
      • Alerts: 跟上面很像,但不見得會卡操作動線,通常伴隨著驚嘆號
      • Carousel/tabs: 前者是左右滑動的投影片,後者是點到才會顯示內容的頁籤
      • Collapsible content that needs to be removed afterward: 想不到
    • 事不宜遲,我們來用 ng-bootstrap 來建立一個 Modal

      • 首先建立 Component(喂,都應該要會背啦)

          ng g c nodes-detail
      • 接著填入 Controller 內容

          import { Component, Input } from '@angular/core';
          import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
        
          @Component({
            selector: 'app-nodes-detail',
            templateUrl: './nodes-detail.component.html',
            styleUrls: ['./nodes-detail.component.css']
          })
          export class NodesDetailComponent {
        
            @Input() node;
        
            constructor(public activeModal: NgbActiveModal) { }
        
            isDanger(prop) {
              return this.node[prop].used / this.node[prop].available > 0.7;
            }
        
            getType(prop) {
              return this.isDanger(prop) ? 'danger' : 'success';
            }
          }
        
        • 在這邊我們用到了 ng-bootstrap 的 NgbActiveModal,它是一個很有用的 service,負責控制 NodesDetailComponent 的載入
        • 跟截至目前為止的 Component 不一樣,既不會在 Parent Component 中使用,亦不會以 Content Child 的方式插入,僅管如此它仍然需要資料,否則怎麼產出內容呢?所以有一個 @Input 屬性
      • 接著是 Template

          <div class="modal-header">
            <button
              type="button"
              class="close"
              aria-label="Close"
              (click)="activeModal.dismiss()"
            >
              <span aria-hidden="true">×</span>
            </button>
            <h4 class="modal-title">{{ node.name }}</h4>
          </div>
          <div class="modal-body container">
            <div class="col-xs-6">
              <app-metric [used]="node.cpu.used" [available]="node.cpu.available">
                <metric-title>CPU</metric-title>
              </app-metric>
            </div>
            <div class="col-xs-6">
              <app-metric [used]="node.mem.used" [available]="node.mem.available">
                <metric-title>Memory</metric-title>
              </app-metric>
            </div>
          </div>
        • 好多好多沒看過的東西啊~不過都是 ng-bootstrap 特有的啦,看過就好,不會用的話再去它的官網
        • 這個 NodeDetail 主要就是有標題,一個 Close 按鈕,再來就是顯示兩個 MetricComponent 當作主要的 body
        • 比較特別的地方是在按下 Close 的時候會呼叫 activeModal.dismiss(),這是前面 Controller 所建立的 service
        • 最後就是 node 這個 property,僅管它是 Component 唯一的 @Input,但所有的內容都靠它長出來
      • 但要怎麼動態載入 NodeDetailsComponent 呢? 首先要修改 Nodes-row Component 的 button, 綁定它的 click 事件

         <td><button class="btn btn-secondary" (click)="open(node)">View</button></td>
      • 事件定義了,就要趕快在 controller 實作 open 這個方法了!

          import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
          import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
          import { NodesDetailComponent } from '../nodes-detail/nodes-detail.component';
        
          @Component({
            selector: '[app-nodes-row]',
            templateUrl: './nodes-row.component.html',
            styleUrls: ['./nodes-row.component.css'],
            changeDetection: ChangeDetectionStrategy.OnPush
          })
          export class NodesRowComponent {
        
            constructor(private modelService: NgbModal) { }
        
            @Input() node: any;
        
            isDanger(prop) {
              return this.node[prop].used / this.node[prop].available > 0.7;
            }
        
            open(node) {
              const modal = this.modelService.open(NodesDetailComponent);
              modal.componentInstance.node = node;
            }
          }
        • 這兒的主角是 NgbModal,看它的命名多棒,前面的叫 NgbActiveModal,這裡的就不含 Active
        • 兩者的差異在於 NgbModal 負責建立 Modal,而 NgbActiveModal 負責取得那個 modal 的參考
        • 怎麼開窗?根據 ng-bootstrap 的文件,傳入要打開的 Component 就對了,所以當 open 這個方法被呼叫時,NodeDetailComponent 被傳入,接著立刻透過 componentInstance 這個屬性傳遞資料 node
      • 除了開跟關之外,開在哪也很重要,因此我們要在 DashboardComponent 的 template 裡定義一個 template

          <template ngbModalContainer></template>
        • template 是 HTML5 的東西,詳情請看MDN
        • 承上,它就是一個類似 placeholder 的存在,搭配著 ng-bootstrap 的 Directive ngbModalContainer,就可以決定 NodeDetail 要顯示在哪了(註: 這是 ng-bootstrap 特有的做法)
      • 最後的最後一步,就是要在 app.component.ts 宣告會動態出現的 Component,把它加到 entryComponents 這個陣列就對了

         entryComponents: [NodesDetailComponent]
        • 這個 entryComponents 跟 declarations, imports, providers, bootstrap, schemas 是同一個層級的
        • 承上,Angular CLI 預設會在編譯時最佳化,所以每一個 Component 的 Factory 都不會被建立,直到真正要載入它的時候為止,但至少它是個確定會出現的東西
        • 然而,動態建立的 Component 就是因為出現的時間點不確定,所以必須事先透過 entryComponents 告知要先備好其 Factory class
  • 小結:

    • 此篇透過 ng-bootstrap 動態建立了 modal
      • 下一篇將不透過任何 UI library 建立 modal
      • 記得睡飽再看下一篇,因為它會動用到很多 Angular 相對低階的 API
      • 註: 低階就是比較困難的意思啦
Angular # 23 - 深入 Components [6]
Angular #21 - 深入 Components [4]

相關文章

 

評論

尚無評論
已經注冊了? 這裡登入
Guest
2025/05/22, 週四

Captcha 圖像