Angular PrimeNG Table sticky 案例

葬天尘

PrimeNG Table Sticky 的用法

  • 先牢骚,官方给的代码用在 Angular8RxJS6 版本上有兼容的问题,主要是 RxJS6 有很多 breaking changes,下面贴出来自己验证过成功的代码 - 2019年7月30日 13:15:31
  • 制作指令:

    
    import { Directive, Input, Renderer, ElementRef, AfterViewInit, OnDestroy, OnChanges } from '@angular/core';
    import { Subscription,fromEvent } from 'rxjs';
    
    @Directive({
        selector: '[stickyHeader]'
    })
    
    export class StickyHeaderDirective implements AfterViewInit, OnDestroy, OnChanges{
    
        private windowScrollSubscription: Subscription = null;
        private windowResizeSubscription: Subscription = null;
        private header: any = null;
        private offsetTop: number = 0;
        private lastScroll: number = 0;
        private isSticky: boolean = false;
        private hasHeader: boolean = false;
        private headerTop = 0;
        @Input('stickyClass') stickyClass: string = "";
        @Input('stickyTop') stickyTop: number = 0;
    
        constructor(private elementRef: ElementRef, private renderer: Renderer) {
    
        }
    
        ngAfterViewInit(): void {
            setTimeout(()=>{
                this.windowScrollSubscription = fromEvent(window, 'scroll').subscribe(() => this.manageScrollEvent());
                this.windowResizeSubscription = fromEvent(window, 'resize').subscribe(() => this.updateHeaderSize());
                const headers = this.elementRef.nativeElement.getElementsByTagName('TR');
                this.hasHeader = headers.length > 0;
                if (this.hasHeader) {
                    this.header = headers[0];
                    this.headerTop = this.header.getBoundingClientRect()['top'];
                    this._calcPosition();
                }
            }, 0);
        }
    
        ngOnDestroy(): void {
            if (this.windowScrollSubscription){
                this.windowScrollSubscription.unsubscribe();
                this.windowScrollSubscription = null;
            }
            if (this.windowResizeSubscription){
                this.windowResizeSubscription.unsubscribe();
                this.windowResizeSubscription = null;
            }
        }
    
        ngOnChanges(changes)
        {
            if (changes.stickyTop) {
                this._calcPosition();
            }
        }
    
        private _calcPosition(){
            if (this.hasHeader) {
                const scroll = window.pageYOffset;
                if (this.isSticky && scroll >= this.headerTop) {
                    this.header.style.top =  this.stickyTop + 'px';
                }
            }
        }
    
        private manageScrollEvent(): void {
            const scroll = window.pageYOffset;
            if (scroll > this.lastScroll && !this.isSticky && scroll >= this.offsetTop) {
                this.setSticky();
            } else if (scroll < this.lastScroll && this.isSticky && scroll <= this.offsetTop) {
                this.unsetSticky();
            }
            this.lastScroll = scroll;
        }
    
        private setSticky(): void {
            this.isSticky = true;
            this.header.style.position = 'fixed';
            this.header.style.top =  this.stickyTop + 'px';
            this.header.style.display = 'table';
            this.updateHeaderSize();
            this.setClass(true);
        }
    
        private updateHeaderSize(){
            if (this.isSticky) {
                const tableWidth = this.elementRef.nativeElement.getBoundingClientRect()['right'] - this.elementRef.nativeElement.getBoundingClientRect()['left'];
                this.header.style.width = tableWidth + 'px';
                // update size of TH elements
                const thArray = this.elementRef.nativeElement.getElementsByTagName('TH');
                for (let i = 0; i < thArray.length; i++){
                    thArray[i].style.width = tableWidth / thArray.length + "px";
                }
                
            }
        }
    
        private unsetSticky(): void {
            this.isSticky = false;
            this.header.style.position = 'static';
            this.header.style.width = 'auto';
            this.header.style.display = 'table-row';
            this.setClass(false);
        }
    
        private setStyle(key: string, value: string): void {
            this.renderer.setElementStyle(this.header, key, value);
        }
    
        private setClass(add: boolean): void {
            if (this.stickyClass){
                this.renderer.setElementClass(this.header, this.stickyClass, add);
            }
        }
    
    }
  • 将制作的指令导入组件所在模块

图片描述

  • 模板中使用 sticky 指令:

图片描述

上面被注释的属性 [stickyTop]=50 可以指定下拉时列头举例屏幕上面的举例

  • 设置 css 样式:

    .header{
        height: 50px;
        width: 100%;
        background-color:cornflowerblue;
        color: white;
        font-size: 16px;
        font-weight: bold;
        font-family: Arial;
        line-height: 50px;
        text-align: center;
        border-bottom: 1px solid #666666;
        position: fixed;
        z-index: 1;
        top: 0px;
    }
    p-dataTable{
        margin-left: 20px;
        margin-top: 50px;
        margin-right: 20px;
        display: block;
    }
    
    :host /deep/ p-datatable .stickyHeader{
      box-shadow: 2px 2px 1px #888888;
    }
  • 最终效果
    图片描述
  • 纠正官方代码的不兼容问题:
    图片描述
阅读 2k
265 声望
7 粉丝
0 条评论
265 声望
7 粉丝
文章目录
宣传栏