【Angular 6】滚动列表组件的封装

2018-07-22 admin

前言

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

需求

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

分析

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

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

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

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

代码

scroll-table.component.html

<div 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>
  <div 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><div>{{ele.field03}}</div></td>
          <td style="width:12.8%;">{{ele.field04}}</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

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;
        div{
            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也可以的。

原文链接:https://segmentfault.com/a/1190000015721692

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-36542.html

文章标题:【Angular 6】滚动列表组件的封装

相关文章
Angular2-primeNG文件上传模块FileUpload使用详解
近期在学习使用Angular2做小项目,期间用到很多primeNG的模块。 本系列将结合实战总结angular2-primeNG各个模块的使用经验。 文件上传模块FileUploadModule 首先要在使用该组件的模块内导入文件上传模块 ...
2017-03-09
从2014年的发展来展望JS的未来将会如何
&lt;font face=&quot;寰�杞�闆呴粦, Arial, sans-serif &quot;&gt;2014骞达紝杞�浠惰�屼笟鍙戝睍杩呴€燂紝鍚勭�嶈��瑷€灞傚嚭涓嶇┓锛屼互婊¤冻鐢ㄦ埛涓嶆柇鍙樺寲鐨勯渶姹傘€傝繖浜涜��...
2015-11-12
12个你未必知道的CSS小知识
虽然CSS并不是一种很复杂的技术,但就算你是一个使用CSS多年的高手,仍然会有很多CSS用法/属性/属性值你从来没使用过,甚至从来没听说过。 1.CSS的color属性并非只能用于文本显示 对于CSS的color属性,相信所有Web开发人员...
2015-11-12
Vue.js组件tab实现选项卡切换
本文实例为大家分享了vue插件tab选项卡的具体代码,供大家参考,具体内容如下 效果图: 代码如下: &lt;!DOCTYPE html&gt; &lt;html lang=&quot;en&quot;&gt; &lt;head&gt; ...
2017-03-13
ajax为什么令人惊异?ajax的优缺点
使用Ajax的最大优点,就是能在不更新整个页面的前提下维护数据。这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变的信息。 Ajax不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。就像DHT...
2015-11-12
HTML5的5个不错的开发工具推荐
HTML5规范终于在今年正式定稿,对于从事多年HTML5开发的人员来说绝对是一个重大新闻。数字天堂董事长,DCloud CEO王安也发表了文章,从开发者和用户两个角度分析了HTML对两个人群的优势。其实,关于HTML5的开发工具,我们以往的...
2015-11-12
JavaScript教程:JS中的原型
Keith Peters 几年前发表的一篇博文,关于学习没有“new”的世界,其中解释了使用原型继承代替构造函数。两者都是纯粹的原型编码。 标准方法(The Standard Way) 一直以来,我们学习的在 JavaScript 里创建对...
2015-11-12
AJAX的浏览器支持
AJAX 的要点是 XMLHttpRequest 对象。 不同的浏览器创建 XMLHttpRequest 对象的方法是有差异的。 IE 浏览器使用 ActiveXObject,而其他的浏览器使用名为 XMLHttpRequest 的 Jav...
2015-11-12
Riot.js:不足1KB的MVP客户端框架
Riot.js是一款MVP(模型-视图-呈现)开源客户端框架,其最大的特点就是体积非常小,不足1KB,虽然体积小,但它可以帮助用户构建大规模的Web应用程序。 Riot.js是由Moot公司开发,目前最新版本为v0.9.2,遵循MIT开源许...
2016-03-11
typeof、instanceof和contructor的区别
typeof:以字符串的形式返回变量的原始类型,typeof在两种情况下会返回&quot;undefined&quot;:一个变量没有被声明的时候,和一个变量的值是undefined的时候,注意,typeof null也会返回object,...
2015-11-12
回到顶部