Angular Datatable的一些問題


這幾天改bug中發現的一些問題,小結一下。從簡單到復雜逐個講。

angular datatable實質上是對jquery庫的包裝,但包裝后不太好用,定制功能比較麻煩。

1. 基本用法

最簡單的用法,大致就是template里:

<table datatable [dtOptions]="dtOptions">

component里:

dtOptions: DataTables.Settings;

最基本的就這兩句,其他代碼都不用改,table就自然有了搜索,按列排序等功能。

2. css

如果要用分頁功能,一是dtOptions需配置一下:

dtOptions: DataTables.Settings = {paging: true};

 二是需要在angular.json里加上css路徑,不然分頁欄排版錯亂:

            "styles": [
              "node_modules/datatables.net-dt/css/jquery.dataTables.css",

3. 去掉(不顯示)上方每頁顯示N條記錄的下拉選擇框

dtOptions配置:

dtOptions: DataTables.Settings = {lengthChange: false;}

4.避免和已有的css沖突

原來的table已經定義了一套css,加上datatable屬性后,原有的css被破壞了。我用個笨辦法,手工把上面那個jquery.dataTables.css的內容拷到另外一個文件,然后注釋掉不需要的部分后再引用。但這樣做了之后,發現表頭和表體各列還是對不齊,最后發現還是要配置dtOptions:

dtOptions: DataTables.Settings = {autoWidth: false;}

5. 數據刷新的問題

這個折騰了不少時間。開始用dtTrigger,即template里:

<table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger">

component里:

dtTrigger: Subject<any> = new Subject<any>();

this.someWebservice.someMethod().subscribe(data => {
    ...
    this.dtTrigger.next();
});

問題是刷新數據時就會提示datatable不能再次初始,比較簡單的解決方法是配置一下dtOptions:

dtOptions: DataTables.Settings = { destroy: true};

這樣設置后,不再報錯。問題是按列排序時,發現數據沒有刷新,還是用的老數據。試了不少方法都不行,最后只能把table放在ViewChild里,在父部件里傳數據,這樣dtTrigger就不需要了。

6. 排序問題

1)日期列排序不正確,解決辦法就是設置dtOptions:

dtOptions: DataTables.Settings = {"columnDefs": [{ "targets": [5, 6], "type": "date" }]};

2) 有一個列,是金錢類型,本來沒什么,但是它有個特殊的格式,凡是負數,不顯示負號,而是用括號來表示,如($5.00)。這樣一來,缺省的排序就出錯了。花了不少時間,最后發現解決辦法是利用dtOptions的render屬性:

dtOptions: DataTables.Settings = {"columnDefs": [{        "targets": [9, 10], "render": (data, type, row) => {
          if (type == 'display') {
             return this.minusSignPipe.transform(this.currencyPipe.transform(data));
          }

          return Number(data);
        }]};

上面的currencyPipe是angular自帶的,minusSignPipe是我們自己寫的,把負號轉成括號。

3) 學會了上面render的用法,解決了幾個列的排序問題,但最后有兩個列,用這個方法無法解決。這個列的html代碼是:

<td><a href="javascript:void(0)" (click)="foo(row.id)" *ngIf="row?.bar> 0">{{row?.bar | currency  | minusSignPipe}}</a></td>

如果用render,排序是解決了,問題是點擊無反應,無法觸發component里的foo()方法。花了不少時間,最后想到利用javascript變通實現:

<td><a href="javascript:void(0)" onclick="document.getElementById('btnFoo').click();" *ngIf="row?.bar > 0">
{{row?.bar | currency | minusSignPipe}}</a></td> <button id="btnFoo" style="display:none" (click)="foo(row.id)">foo</button>

也即利用一個隱藏的button中轉一下,成功觸發foo()方法。因為onclick里不能用{{row?.id}}的方式傳參數,所以只能用變通的方法中轉。之所以用button的click事件,是因為foo()方法里需要將一個事件emit出去,觸發父部件里的方法:

  @Output() emitter = new EventEmitter();
  foo(id) {
    this.emitter.emit(id);
  } 

另外一個列也需要傳遞參數,不過不需要emit,稍微變通了一下,這樣解決:

<td><a href="javascript:void(0)" onclick="window.foo(this.nextSibling.value);" *ngIf="row?.bar && row?.bar != 0">
{{row?.bar | currency | minusSignPipe}}</a><input type="hidden" value="{{row?.id}}" style="width:1px" /></td>

component里:

constructor(
) {
    window['foo'] = (id) => {
      this.bar(id);
    };
}

這個方法不能用於有emit的情況,雖然可以觸發方法,但無法emit出去。

7. drawCallback的應用

dtOptions里有個drawCallback屬性,在排序等操作后會觸發。利用這一特性,解決了一個問題,就是將單數行和雙數行用不同的顏色顯示。開始以為用css就可以了,但發現class="odd"和class="even"經常出現錯亂,極不可靠,特別是排序之后。最后只能用drawCallback。

 dtOptions: DataTables.Settings = {drawCallback: (settings) => {
      let nodes = settings.nTBody.childNodes;
      let count = 0;
      if (settings.aoData[0] != undefined) {
        for (let i = 0; i < nodes.length; i++) {
          if (nodes[i].nodeName == "TR" && nodes[i].hasChildNodes() == true) {
              if (count % 2 == 0) {
                nodes[i].firstChild.parentElement.outerHTML = nodes[i].firstChild.parentElement.outerHTML.replace(/style="background-color:white"/gi, 'style="background-color:#9ddbf2"');
              }
              else {
                nodes[i].firstChild.parentElement.outerHTML = nodes[i].firstChild.parentElement.outerHTML.replace(/style="background-color:#9ddbf2"/gi, 'style="background-color:white"');
              }
              count = count + 1;
            }
          }
        }
      }
    }};  

后來發現這個方法在IE上無效,Chrome可以,因為需要通過WebBrowser控件訪問,只好另外想了個麻煩的方法。template里:

<td><span style="display:none;width:1px">{{row?.serial}}</span>{{row?.id}}</td>

這個隱藏的serial相當於數據列表的索引項,在component里另外用一個數組來保存數據行的順序:

rows = [];
dataList = [];

this.someWebService.someMethod().subscribe(data => {
     let count = 1;
     dataList = data;
     for (let i = 0; i < dataList.length; i++) {
           dataList[i].serial = count;
           rows.push(count);
           count++;
     }
});

然后在drawCallback里給rows數組重新賦值:

dtOptions: DataTables.Settings = {drawCallback: (settings) => {
      let nodes = settings.nTBody.childNodes;
      let count = 0;
      if (settings.aoData[0] != undefined) {
        for (let i = 0; i < nodes.length; i++) {
          if (nodes[i].nodeName == "TR" && nodes[i].hasChildNodes() == true) {
            let str = nodes[i].firstChild.parentElement.outerHTML.toString();
            this.rows[count] = parseInt(nodes[i].firstChild.firstChild.textContent);
            count = count + 1;
          }
        }
      }
    }};

然后在template里綁定一個方法讀取rows數組:

<td [style.background-color]="getRowStyle(row)"><span style="display:none;width:1px">{{row?.serial}}</span>{{row?.id}}</td>

getRowStyle方法:

  getRowStyle(row: Foo) {
    for (let i = 0; i < this.rows.length; i++) {
      if (this.rows[i] == row.serial) {
        if (i % 2 == 0) {
          return "white";
        }
        else {
          return "#9ddbf2";
        }
      }
    }
  }

方法是比較笨,但一時也想不出好辦法。不過從中也體會到一些drawCallback的應用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM