Angular實現虛擬滾動多選下拉框筆記

要求:

實現一個angular多選下拉框組件,當有超過2000個選項時,滑動/挑選/全選均不卡。css

正篇:

爲了方便,這裏不考慮擴展性,因此規定下拉框展開只顯示7行數據,行高27pxhtml

約定:input爲一個叫作list的SelectItem[],SelectItem大概定義以下:

export class SelectItem {    
    public label: string;    
    public value: string;    
    public disabled?: boolean; // 某些選項禁用 
    public groupKey?: string; // 給選項分組用 
    public groupHead?: string; // 顯示選項組 
    public checked?: boolean; // 是否打勾 
    public tempChecked?: boolean; // 是否臨時打勾(用戶要求,自動保存選項的話不須要) 
    public originalCheckStatus?: boolean; // 放棄保存(用戶要求,自動保存選項的話不須要)
}複製代碼

每次咱們給用戶看到的選項只有6個,因此當整個數組進來時咱們最少只用截取6個;又由於規定每條高度27px,因此包含選項的框162px

this.actualList = this.list.slice(0, 8); // 多截兩個當緩衝複製代碼

.list-container {height: 162px;}複製代碼

因此在html畫出來時用戶就恰好看見6個 (trackBy是額外的優化)

<div class="list-container"> <div *ngFor="let item of actualList; trackBy: trackByValue" class="inner-option"> <label> <input type="checkbox" [(ngModel)]="item.checked"> <span>{{item.label}}</span> </label> </div> <div>複製代碼

可這個時候滑動條是滿的不能滑動,咱們須要給這個面板一個假的高度把面板撐開

<div class="list-container">
    <div [style.height]="scrollHeight">
        <div *ngFor="let item of actualList; trackBy: trackByValue" class="inner-option">
            <label>
                <input type="checkbox" [(ngModel)]="item.checked">
                <span>{{item.label}}</span>
            </label>
        </div>
    </div>
</div>複製代碼

this.scrollHeight = this.list.length * 27;複製代碼

.list-container {
    height: 162px;
    overflow: auto; // 固然你要讓這個外層溢出後加滾動
}複製代碼

這樣你就獲得了一個看起來能夠滾動的可是其實只有第一頁的下拉框。而後你要作的就是在用戶滾動以後動態的更新actualList,這裏咱們能夠用angular封裝好的scroll event:前端

<div class="list-container" (scroll)="onScroll($event)">
    <div [style.height]="scrollHeight">
        <div *ngFor="let item of actualList; trackBy: trackByValue" class="inner-option">
            <label>
                <input type="checkbox" [(ngModel)]="item.checked">
                <span>{{item.label}}</span>
            </label>
        </div>
    </div>
</div>複製代碼

public onScroll(e) {
    const firstIndex = Math.floor(e.target.scrollTop / 27);
    const secondIndex = firstIndex + 8;
    this.actualList = this.list.slice(firstIndex, secondIndex);
}複製代碼

以上雖然你是更新了能夠顯示的實際list,可是尚未在面板上顯示出來,由於這個actualList被你劃上去了,因此接下來須要在滾動時動態的把這一塊往下/上移,這裏咱們用translateYnode

public onScroll(e) {
...
this.translateY = `translateY(${firstIndex * 27}px)`;
}複製代碼

<div>
...
    <div *ngFor=....class="inner-option" [style.transform]="translateY">
...
</div>複製代碼

其實到這裏核心的整個虛擬滾動的列表就製做完成了,剩下的工做包括:react

  1. 並非每一點滾動都須要從新計算actualList的,只有最上面的那個滾出去了以後才須要從新render
  2. 這裏每次從新獲得actualList以後瀏覽器要移除並添加節點在dom上,最優的狀況應該是重用這些節點,讓第一個和最後一個節點來回移動
  3. check all怎麼跟下面的子checkbox同步起來,每一個group的checkbox怎麼跟其餘checkbox同步起來
  4. 下拉框的按鈕怎麼顯示用戶選好的項目
  5. 怎麼放棄選擇,點ok保存
  6. 怎麼區分用戶正在點下拉框外面(關下拉框)仍是裏面正常操做
  7. 怎麼實時監聽輸入的list的變化,用戶的選擇變化

下篇再說typescript

---------------------------------其餘-----------------------------bootstrap

背景:數組

目前市面上的不少下拉框(包括bootstrap的各類angular/react實現,kendoUI,antd)都尚未考慮大數據量的狀況。這樣的話一旦數據突破4000條(單選)/1000(多選),瀏覽器就會變卡,由於DOM tree已經繪製了全部這些nodes。瀏覽器

吐槽:bash

這種狀況不多見,但不是沒有,好比咱們的場景是容許用戶建立本身的條目,一旦共享以後這條項目就會出如今某個多選框裏讓其餘人自由組合使用,數據量就很容易變大。

發現:

Angular更新7的時候我注意到他們的material design庫新增長了一個新玩意:虛擬滾動。本質上就是隻繪製用戶看到的節點去極大地節省內存開銷。blog.angular.io/version-7-o…

過了不久後看見了阿健大叔在前端之巔發表的《如何用react+rxjs實現一個虛擬滾動組件》受啓發,就決定用這種思想重寫咱們正在用的angular多選下拉框。

相關文章
相關標籤/搜索