Angular Datatable的一些问题

这几天改bug中发现的一些问题,小结一下。从简单到复杂逐个讲。

angular datatable实质上是对jquery库的包装,但包装后不太好用,定制功能比较麻烦。

1. 基本用法

最简单的用法,大致就是template里:

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

component里:

1
dtOptions: DataTables.Settings;

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

2. css

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

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

 二是需要在angular.json里加上css路径,不然分页栏排版错乱:

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

3. 去掉(不显示)上方每页显示N条记录的下拉选择框

dtOptions配置:

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

4.避免和已有的css冲突

原来的table已经定义了一套css,加上datatable属性后,原有的css被破坏了。我用个笨办法,手工把上面那个jquery.dataTables.css的内容拷到另外一个文件,然后注释掉不需要的部分后再引用。但这样做了之后,发现表头和表体各列还是对不齐,最后发现还是要配置dtOptions:

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

5. 数据刷新的问题

这个折腾了不少时间。开始用dtTrigger,即template里:

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

component里:

1
2
3
4
5
6
dtTrigger: Subject<any> = new Subject<any>();
 
this.someWebservice.someMethod().subscribe(data => {
    ...
    this.dtTrigger.next();
});

问题是刷新数据时就会提示datatable不能再次初始,比较简单的解决方法是配置一下dtOptions:

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

这样设置后,不再报错。问题是按列排序时,发现数据没有刷新,还是用的老数据。试了不少方法都不行,最后只能把table放在ViewChild里,在父部件里传数据,这样dtTrigger就不需要了。

6. 排序问题

1)日期列排序不正确,解决办法就是设置dtOptions:

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

2) 有一个列,是金钱类型,本来没什么,但是它有个特殊的格式,凡是负数,不显示负号,而是用括号来表示,如($5.00)。这样一来,缺省的排序就出错了。花了不少时间,最后发现解决办法是利用dtOptions的render属性:

1
2
3
4
5
6
7
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代码是:

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

如果用render,排序是解决了,问题是点击无反应,无法触发component里的foo()方法。花了不少时间,最后想到利用javascript变通实现:

1
2
3
<td><a href="javascript:void(0)" onclick="document.getElementById('btnFoo').click();" *ngIf="row?.bar > 0"><br>{{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出去,触发父部件里的方法:

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

另外一个列也需要传递参数,不过不需要emit,稍微变通了一下,这样解决:

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

component里:

1
2
3
4
5
6
constructor(
) {
    window['foo'] = (id) => {
      this.bar(id);
    };
}

这个方法不能用于有emit的情况,虽然可以触发方法,但无法emit出去。

7. drawCallback的应用

dtOptions里有个drawCallback属性,在排序等操作后会触发。利用这一特性,解决了一个问题,就是将单数行和双数行用不同的颜色显示。开始以为用css就可以了,但发现class="odd"和class="even"经常出现错乱,极不可靠,特别是排序之后。最后只能用drawCallback。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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里:

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

这个隐藏的serial相当于数据列表的索引项,在component里另外用一个数组来保存数据行的顺序:

1
2
3
4
5
6
7
8
9
10
11
12
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数组重新赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
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数组:

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

getRowStyle方法:

1
2
3
4
5
6
7
8
9
10
11
12
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的应用。

 

posted @   平静寄居者  阅读(1224)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示