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多选下拉框。

相关文章
相关标签/搜索