视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
对Angular6中滚动列表组件的封装的分析
2020-11-27 19:33:21 责编:小采
文档


这篇文章给大家分享的内容是关于对Angular 6中滚动列表组件的封装的分析,有一定的参考价值,有需要的朋友可以参考一下。

前言

学习应为inputoutput相结合之过程,这就是写这篇文章的原因。
在大屏幕展示web APP中,经常会用到滚动列表。经过几次尝试,确定了一个还不错的思路。

需求

  • 列表表头thead部分静止,而tbody部分向上滚动。

  • tbody部分滚动结束之后,需要刷新数据,最终效果是以向上滚动的形式将数据库中全部相关数据展示出来。

  • 分析

    如果数据量比较小的话,我们完全可以将数据一次性全部拿出来,放到DOM中进行循环滚动。实际就是类似轮播图的效果。

    但若有很多数据的话,这样做很可能造成内存泄露。自然,我们可以想到将列表数据分页。我最初的想法是,在table的外层放一个p作为容器,然后table定时向上增加top值,等table跑了一半时,向后端请求数据,动态创建一个组件tbody插入到table中,然后等前面一个tbody走完时(看不见了),将这个组件删除。该想法看起来可行的,但是实践中遇到了不少麻烦。在删除前面的组件时,会导致table的高度减小,表格瞬间掉下去了。这显然不是我们想要的,副作用挺大的。

    既然这样,我把tbody分开到两个table里,两个table循环。当前一个table下面没有数据时,第二个table开始走,等第一个table完全走出p,将它位置重置到p的下面,并更新数据,然后重复之间的动作。完成起来稍微有点麻烦,不过效果还说得过去,差强人意。问题是,两个定时器不稳定,打开其他软件,再回来时,两个table跑的不一致了。这个先天性疾病,setInterval就是不够精确的,两个定时器一起容易出现配合不好的情况。

    最终,在下班回家的路上,我想到了一个不需要两个table的方法。只用一个table定时上移,走完一半时,清除定时器,重置位置,并更新一半的数据。也就是去除数组中前一半数据,将后台拉过来的新数据拼接在数组上。这样就可以实现数据的持续刷新,并且table看起来是一直往上走的。

    代码

    scroll-table.component.html

    <p class="table-container">
     <table class="head-show">
     <thead>
     <tr>
     <th style="width:12.8%;">字段1</th>
     <th style="width:12.8%;">字段2</th>
     <th>字段3</th>
     <th style="width:12.8%;">字段4</th>
     </tr>
     </thead>
     </table>
     <p class="scroller-container">
     <table #scroller class="scroller">
     <tbody>
     <tr *ngFor="let ele of tbody">
     <td style="width:12.8%;">{{ele.field01}}</td>
     <td style="width:12.8%;">{{ele.field02}}</td>
     <td><p>{{ele.field03}}</p></td>
     <td style="width:12.8%;">{{ele.field04}}</td>
     </tr>
     </tbody>
     </table>
     </p>
    </p>

    scroll-table.component.ts

    import { Component, OnInit, ViewChild, ElementRef, Input } from '@angular/core';
    import { HttpService } from '../http.service';
    
    @Component({
     selector: 'app-scroll-table',
     templateUrl: './scroll-table.component.html',
     styleUrls: ['./scroll-table.component.scss']
    })
    export class ScrollTableComponent implements OnInit {
     tbody: any = [];
     @Input() url; //将地址变成组件的一个参数,也就是输入属性
     //控制滚动的元素
     @ViewChild('scroller') scrollerRef: ElementRef;
     timer: any;
    
     freshData: any;
    
     pageNow = 1;//pageNow是当前数据的页码,初始化为1
    
     constructor(private http: HttpService) {}
    
     ngOnInit() {
     //初始化拿到native
     let scroller: HTMLElement = this.scrollerRef.nativeElement;
     this.http.sendRequest(this.url).subscribe((data :any[]) => {
     
     this.tbody = data.concat(data);
     });
     //开启定时器
     this.timer = this.go(scroller);
     }
    
     getFreshData() {
     //每次请求数据时,pageNow自增1
     this.http.sendRequest(`${this.url}?pageNow=${++this.pageNow}`).subscribe((data:any[]) => {
     if(data.length<10) {
     //数据丢弃,pageNow重置为1
     this.pageNow = 1;
     }
     this.freshData = data;
     });
     }
     
     go(scroller) {
     var
     moved = 0,
     step = -50,
     timer = null,
     task = () => {
     let style = document.defaultView.getComputedStyle(scroller, null);
     let top = parseInt(style.top, 10);
     if (moved < 10) {
     if(moved===0) {
     this.getFreshData();
     }
     scroller.style.transition = "top 0.5s ease";
     moved++;
     scroller.style.top = top + step + 'px';
    
     } else {
     //重置top,moved,清除定时器
     clearInterval(timer);
     moved = 0;
     scroller.style.transition = "none";
     scroller.style.top = '0px';
     //更新数据
     this.tbody = this.tbody.slice(10).concat(this.freshData);
     timer = setInterval(task,1000);
     }
     };
     timer = setInterval(task, 1000);
     }
    }

    scroll-table.component.scss

    .table-container {
     width: 100%;
     height: 100%;
    }
    .head-show {
     border-top: 1px solid #4076b9;
     height: 11.7%;
    }
    .scroller-container {
     border-bottom: 1px solid #4076b9;
     //border: 1px solid #fff;
     width: 100%;
     //height: 88.3%;
     height: 250px;
     box-sizing: border-box;
     overflow: hidden;
     position:relative;
     .scroller {
     position: absolute;
     top:0;
     left:0;
     transition: top .5s ease;
     }
    }
    table {
     width: 100%;
     border-collapse: collapse;
     table-layout: fixed;
     //border-bottom:1px solid #4076b9;
     th {
     
     border-bottom:1px dashed #2d4f85;
     color:#10adda;
     padding:8px 2px;
     font-size: 14px;
     }
     td {
     border-bottom: 1px dashed #2d4f85;
     font-size: 12px;
     
     color:#10adda;
     position: relative;
     height: 49px;
     p{
     padding:0 2px;
     box-sizing: border-box;
     text-align:center;
     display: table-cell;
     overflow: hidden;
     vertical-align: middle;
     }
     //border-width:1px 0 ;
     }
    }

    这样实现的效果是,该组件只需要传入一个参数url,然后所有的操作、包括更新数据,全部由组件自身完成。从而完成了组件的封装,便于复用。

    总结和思考

    1、更新数据应该放在源头更新,也就是说,不要去添加和删除DOM元素,这样操作麻烦,性能也低。放在源头的意思是,在组件类中存储展示数据的那个数组上做文章。
    2、后台请求新数据应该提早准备就绪,放在另一个临时数组中。它相当于一个缓存,一个暂存器。
    3、我将组件想象成一个函数,它只有一个参数,就是数据的地址,只要有这个参数,组件就能正常工作,不依赖于其他任何值。松耦合性。
    4、加强函数式编程思想,虽然这是React的特色,但我总觉得angular也可以的。

    相关推荐:

    AngularJs自定义指令可以如何来设置以及自定义指令的命名规范

    AngularJs中model、Controller(控制器)和View(视图)之间有什么样的关系?(图文)

    下载本文
    显示全文
    专题