【Angular 6】滾動列表組件的封裝

前言

學習應爲inputoutput相結合之過程,這就是寫這篇文章的緣由。
在大屏幕展現web APP中,常常會用到滾動列表。通過幾回嘗試,肯定了一個還不錯的思路。css

需求

  • 列表表頭thead部分靜止,而tbody部分向上滾動。
  • tbody部分滾動結束以後,須要刷新數據,最終效果是以向上滾動的形式將數據庫中所有相關數據展現出來。

分析

若是數據量比較小的話,咱們徹底能夠將數據一次性所有拿出來,放到DOM中進行循環滾動。實際就是相似輪播圖的效果。html

但如有不少數據的話,這樣作極可能形成內存泄露。天然,咱們能夠想到將列表數據分頁。我最初的想法是,在table的外層放一個div做爲容器,而後table定時向上增長top值,等table跑了一半時,向後端請求數據,動態建立一個組件tbody插入到table中,而後等前面一個tbody走完時(看不見了),將這個組件刪除。該想法看起來可行的,可是實踐中遇到了很多麻煩。在刪除前面的組件時,會致使table的高度減少,表格瞬間掉下去了。這顯然不是咱們想要的,反作用挺大的。web

既然這樣,我把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,而後全部的操做、包括更新數據,所有由組件自身完成。從而完成了組件的封裝,便於複用。後端

總結和思考

一、更新數據應該放在源頭更新,也就是說,不要去添加和刪除DOM元素,這樣操做麻煩,性能也低。放在源頭的意思是,在組件類中存儲展現數據的那個數組上作文章。
二、後臺請求新數據應該提前準備就緒,放在另外一個臨時數組中。它至關於一個緩存,一個暫存器。
三、我將組件想象成一個函數,它只有一個參數,就是數據的地址,只要有這個參數,組件就能正常工做,不依賴於其餘任何值。鬆耦合性。
四、增強函數式編程思想,雖然這是React的特點,但我總以爲angular也能夠的。數組

相關文章
相關標籤/搜索