基於列表類開發的列表組件

主要實現的功能

​ 排序、分頁、過濾、自定義操做列、列表中根據數據展現相應圖標、跳轉詳情頁html

組件繼承思想

​ 封裝一個列表基類,派生類繼承該類,複用基類的功能。git

Constructor 構造函數

​ 若是派生類未聲明構造函數,它將使用基類的構造函數。這意味着在基類構造函數注入的全部服務,子組件都能訪問到。github

生命週期方法不繼承

​ 若是基類中包含生命週期鉤子,如 ngOnInitngOnChanges 等,而在派生類沒有定義相應的生命週期鉤子,基類的生命週期鉤子會被自動調用。若是須要在派生類觸發ngOnInit,則須要在派生類上定義相應的生命週期鉤子。編程

繼承的方法和屬性基於可訪問性級別

​ 派生類不能訪問私有方法和屬性,僅繼承公共方法和屬性。api

模板是不能被繼承

​ 模板是不能被繼承的 ,派生類需自定義模板,所以共享的 DOM 結構或行爲須要單獨處理。bash

元數據和裝飾器不繼承

裝飾器和元數據(@Component@Directive@NgModule等),這些元數據和裝飾器不會被派生類繼承,可是有一個例外,@Input@Output裝飾器會傳遞給派生類app

依賴注入

派生類必須經過調用super注入基類的參數異步

實現過程

application列表組件和基類ResourceListBaseResourceListWithStatuses 關係異步編程

application列表組件主要繼承父類ResourceListWithStatuses,經過重寫ResourceListWithStatuses的方法,自定義模板,來複用父類的功能屬性。函數

組件定義

自定義模板
@Component({
  selector: 'sym-application-list',
  templateUrl: './application.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
複製代碼
繼承父類

經過expends繼承父類

重寫父類方法

經過繼承基類ResourceListWithStatuses,能夠複用其基本的功能屬性,同時能夠定義屬性和方法擴展列表組件的功能,實現這個列表組件主要的是若是調用組件方法發起請求,獲取到數據

  • 重寫getResourceObservable 方法,該方法主要是返回一個Observable,進行異步編程。

    在這個方法裏,能夠請求多個api,使用Observable.forkJoin,能夠合併多個Observable,返回一個Observable。

  • 結合sym-select 組件搜索

sym-select組件的namespace下拉列表的數據nsList是異步獲取的,若是是選擇是的namespace是ALL,那麼列表組件要獲取到nsList,而後對每一個nsList裏的namespace進行所有請求。若是選擇的是某個namesapce,那麼就傳入namespace的值,進行單個請求

這裏也是比較疑惑的地方,以前想經過@Input傳入屬性的方式,來把搜索組件的數據傳遞給applicaiton列表組件sym-application-list

可是嘗試以後,發如今頁面初始化的時候,經過@Input的屬性傳入的nsList爲空的。若是傳入的屬性是同步獲取的,則能夠傳遞到application列表組件中

最後獲取搜索參數的方式,採起服務依賴注入的方式來獲取。

  • 經過重寫map 方法,在自定義表格的數據,返回所需表格的數據。
ngOnInit() {
    this.data_.sortingDataAccessor = (item, property) => {
      switch (property) {
        case 'name': return item.objectMeta.name;
        case 'creationTimestamp': return item.status.creationTimestamp;
        case 'namespace': return item.objectMeta.namespace;
        default: return item[property];
      }
    };
  }

  getResourceObservable(params?: HttpParams): Observable<ApplicationList> {
    const res = this.localtionService_.onNamespaceUpdate.subscribe(() => {
      this.namespace = this.localtionService_.current().namespace;
      this.nsList = this.localtionService_.current().namespaceList;
    });
    const data: any = {
      items: [],
      listMeta: {
        totalItems: 0
      }
    };
    if (this.namespace === 'ALL') {
      const list = this.nsList.slice(1);
      const observableList = list.map((ns: any) => {
        return this.application_.get(this.endpoint, undefined, ns.name);
      });
      if (observableList && observableList.length === 0) {
        return new Observable((observer) => {
          observer.next(data);
        });
      }
      return new Observable((observer) => {
        Observable.forkJoin(observableList).subscribe((res: any) => {
          res.map((item: any, index: number) => {
            item.items = item.items.map((v: any) => {
              v.objectMeta.namespace = list[index].name;
              return v;
            });
            data.items = data.items.concat(item.items);
            data.listMeta.totalItems += item.listMeta.totalItems;
          });
          observer.next(data);
        });
      });
    } else if (this.namespace !== undefined) {
      return this.application_.get(this.endpoint, undefined, this.namespace, params);
    }
    return new Observable((observer) => {
      observer.next(data);
    });
  }

  map(applicationList: ApplicationList): Application[] {
    if (this.namespace !== 'ALL') {
      applicationList.items = applicationList.items.map((v: any) => {
        v.objectMeta.namespace = this.namespace;
        return v;
      });
    }
    console.log('applicationList', applicationList);
    return applicationList.items;
  }

  isInErrorState(resource: Application): boolean {
    return resource.status ? (resource.status.replicas && resource.status.replicas !== resource.status.availableReplicas ?
       true : false) : false;
  }

  isInSuccessState(resource: Application): boolean {
    return resource.status ? (resource.status.replicas && resource.status.replicas === resource.status.availableReplicas ?
       true : false) : false;
  }

  protected getDisplayColumns(): string[] {
    return ['statusicon', 'name', 'namespace', 'labels', 'replicas', 'creationTimestamp'];
  }
複製代碼

組件使用

<sym-application-list #applicationList></sym-application-list>
複製代碼

初始化時,調用getList請求方法

this.applicationList.getList();
複製代碼

公共組件的巧妙之處

actionColumn 操做列能夠自定義

在基類中已有聲明註冊操做列的方法,在application列表組件繼承該方法,在構造函數中,註冊操做列,MenuComponent是引入的組件

this.registerActionColumn<MenuComponent>('menu', MenuComponent);
複製代碼
過濾功能

這裏的Pods列表組件也是繼承了ResourceListWithStatuses

繼承的組件都會繼承過濾功能,由於繼承的組件的模板是自定義的,能夠在模板上引入過濾的組件

<kd-card-list-filter></kd-card-list-filter>
複製代碼

基類中以判斷是否有過濾組件,並訂閱過濾組件的方法觸發列表過濾

ngAfterViewInit(): void {
    this.data_.sort = this.matSort_;
    this.data_.paginator = this.matPaginator_;

    if (this.cardFilter_) {
      // 表格搜索
      this.cardFilter_.filterEvent.subscribe(() => {
        const filterValue = this.cardFilter_.query;
        this.data_.filter = filterValue.trim().toLowerCase();
      });
    }
  }
複製代碼

最後

​ 經過繼承一些列表組件或者基類,能夠大大地提高組件功能的複用率,而且可以提升開發效率。在一些業務場景中,咱們總會面臨着,多處同樣的代碼,若是有變更,就要所有更改,這樣不利於代碼的簡潔,並且代碼邏輯相對單一。由此咱們要學會封裝一些經常使用性的組件,來達到代碼的複用率,增強代碼的健壯性。

部分代碼

相關文章
相關標籤/搜索