all files / ScrollingLoad/ scrolling-load.component.ts

41.51% Statements 22/53
0% Branches 0/22
9.09% Functions 1/11
41.67% Lines 20/48
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156                                                                                                                                                                                                                                                                                
import {
    Component,
    Input,
    Output,
    EventEmitter,
    OnInit,
    AfterViewChecked,
    ElementRef,
    ViewChild,
    HostListener
} from '@angular/core';
 
import {
    BasePaginatedResponse,
    PaginatedRequest
} from 'ivy.angular.data';
 
import { MathHelper } from 'ivy.angular.value-helpers';
 
 
/**
 * This component works in the following manner:
 * There are 2 separate things that can fire a pagination update
 *
 * If sthe content div is larger than the container div, scroll functionality will be enabled.
 * When you scroll to the bottom of the overflow, the pagination will execute.
 *
 * Alternatively, there is an issue that occurs when the content div is not larger than the container div.
 * We examine this in the AfterViewChecked event hook.  If the container height == scroll height, then
 * scroll capabilities are not enabled.  As such, we'll continue to fire off pagination updates until either
 * there are no pages or we've begun to enable scroll capabilities by ensuring the content div is larger than
 * the corresponding container div.
 */
@Component({
    selector: 'ivy-scrolling-load',
    templateUrl: './scrolling-load.component.html'
})
export class ScrollingLoadComponent implements OnInit, AfterViewChecked {
 
    @Input()
    maxHeight: number;
 
    @Input()
    countPerLoad: number;
 
    @Input()
    response: BasePaginatedResponse;
 
    @Output()
    onScrollBottom: EventEmitter<PaginatedRequest> = new EventEmitter<PaginatedRequest>();
 
    @ViewChild('scrollcontent') contentDiv: ElementRef;
    @ViewChild('scrollcontainer') containerDiv: ElementRef;
 
 
    constructor(
        private math: MathHelper
        ) {
    }
 
 
    private req: PaginatedRequest = new PaginatedRequest();
    private pageHeight: number;
    private adjustedHeight: number;
 
    //@HostListener('scroll', ['$event'])
    onScroll(event: any): void {
 
        // We only want to fire off pagination if we're not already waiting for a response
        // Otherwise, we will screw up the paginated context
        if (this.response != null) {
 
            // Scroll increment condition
            if (event.target.scrollTop + this.containerDiv.nativeElement.clientHeight == event.target.scrollHeight) {
                this.incrementPage();
            }
        }
    }
 
    ngOnInit(): void {
 
        if (this.countPerLoad != null) {
            this.req.pageCount = this.countPerLoad;
        } else {
 
            // Seems a bit more reasonable than 10
            this.req.pageCount = 5;
        }
 
        this.emitPageReq();
    }
    
    ngAfterViewChecked(): void {
 
        if (this.response == null) return;
 
        // This value will be correct on the first hit after we populate the response
        if (!this.pageHeight) {
            this.pageHeight = this.containerDiv.nativeElement.clientHeight;
        }
 
        // Check increment condition
        // Is our container larger than our contents???
        // If so, we need to try and load more contents
        if (this.pageHeight > this.contentDiv.nativeElement.clientHeight) {
 
            // Are we still waiting on the previous increment to complete???
            if (this.contentDiv.nativeElement.clientHeight != this.adjustedHeight){
 
                this.adjustedHeight = this.contentDiv.nativeElement.clientHeight;
                this.incrementPage();
            }
        }
    }
 
    getHeight(): string {
 
        // This seems to raise an issue with ExpressionChangedAfterItHasBeenCheckedError
        // Not exactly sure why, but first we need to have the max-height 100% to determine page height
        // After we have page height, we can set that as the max height
        if (this.maxHeight == null && !this.pageHeight) {
            return '100%';
        } else if (this.pageHeight) {
            return this.pageHeight + 'px';
        } else { 
           return this.maxHeight + 'px';
        }
    }
    
    private emitPageReq(): void {
        this.onScrollBottom.emit(this.req);
    }
 
    private getTotalPages(): number {
        return this.math.ceil(this.response.totalCount / this.req.pageCount);
    }
 
    private incrementPage(): void {
 
        if (this.req.pageNumber < this.getTotalPages()) {
 
            // Setup loading spinner again
            setTimeout(() => this.nullResponse());
 
            // Ensure we load the next page of data
            this.req.pageNumber++;
 
            // Emit request
            this.emitPageReq();
        }
    }
 
    private nullResponse(): void {
        this.response = null;
    }
}