All files / ngui-list/src ngui-virtual-list.component.ts

100% Statements 26/26
75% Branches 3/4
100% Functions 4/4
100% Lines 24/24

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 1363x                             3x 3x                                                                                 3x             18x   18x                                     18x       18x     18x     18x 18x 18x 18x         5x 5x                   1x 1x   1x   1x   1x 1x   1x           1x 1x        
import {
  AfterViewInit,
  ChangeDetectorRef,
  Component,
  ComponentRef,
  ContentChild,
  ElementRef,
  EventEmitter,
  Output,
  Renderer2,
  TemplateRef,
  ViewChild,
  ViewContainerRef
} from '@angular/core';
 
import { DynamicComponentService } from '../../ngui-utils/src/dynamic-component.service';
import { NguiInviewPageComponent } from './ngui-inview-page.component';
 
/**
 * Virtual List
 *
 * The `<ngui-inview ..>` inserts <ngui-inview-page> into
 * `<div #pages>` when it is in viewport
 * When it's inserted, it will be pushed down, which makes it out of viewport.
 * User scrolls down to see the bottom of the list,
 * then it will insert another `<ngui-inview-page>` again.
 *
 * <ngui-inview-page> listens to (nguiInview) and (nguiOutview) events,
 * when <ngui-inview-page> is out of view port, it empties out the contents.
 * and it restores back the contents when it is in viewport again.
 
 ### Example
 ```html
 <ngui-virtual-list (bottomInview)="loadItems($event)">
   <ng-template let-items="items">
     <div *ngIf="!items">Loading</div>
     <li *ngFor="let num of items; trackBy: num">row number: {{ num }}</li>
   </ng-template>
 </ngui-virtual-list>
 ```
 */
@Component({
  selector: 'ngui-virtual-list',
  template: `
    <div class="ngui-virtual-list"
      (focus)="_focused = true"
      (click)="_focused = true">
      <!-- hold multiple <ngui-inview-page> -->
      <div #pages></div>
      <!-- insert <ngui-inview-page> into #pages -->
    </div>
    <ngui-inview (inview)="addAnInviewPageToPages()"></ngui-inview>
  `,
  styles: [`
    :host {display: block}
  `]
})
export class NguiVirtualListComponent implements AfterViewInit {
 
  /** the container NguiInviewPage will be inserted */
  @ViewChild('pages', { read: ViewContainerRef }) pagesRef: ViewContainerRef;
  /** Template of NguiInviewPage. Allow users to define their own template  */
  @ContentChild(TemplateRef) template: TemplateRef<any>;
  /** Fired when child `<ngui-list-item>` is selected */
  @Output() selected: EventEmitter<any> = new EventEmitter();
  /** Fired when `ESC` key is pressed from `<ngui-list-item>` */
  @Output() escaped: EventEmitter<any> = new EventEmitter();
 
  /**
   * Event fired when bottom of the virtual list is in view
   * The handler of this event must call `$event.addItems(items: Array<any>)` to fill contents
   * If not, only the first page is loaded, and rest of the pages won't be loaded;
 
   ### Example
   ```html
   <ngui-virtual-list (bottomInview)="loadItems($event)">
     <ng-template let-items="items">
       <div *ngIf="items else noItems">
          <li *ngFor="let num of items; trackBy: num">row number: {{ num }}</li>
       </div>
       <ng-template #noItems>Loading</ng-template>
     </ng-template>
   </ngui-virtual-list>
   ```
   */
  @Output() bottomInview: EventEmitter<any> = new EventEmitter();
 
  /** The last NguiInviewPageComponent being inserted */
  inviewPage: ComponentRef<NguiInviewPageComponent>;
  _focused = false;
  /** Indicates if a page is still loading */
  isListLoading: boolean;
  inviewPages: Array<ComponentRef<NguiInviewPageComponent>> = [];
 
  constructor(
    public renderer: Renderer2,
    public element: ElementRef,
    public dynamicComponentService: DynamicComponentService,
    public cdr: ChangeDetectorRef
  ) {}
 
  /** Check if necessary input and output is provided */
  ngAfterViewInit(): void {
    if (!this.template || !this.bottomInview.observers.length) {
      console.error('<ngui-virtual-list> requires [template] and {bottomInview)');
    }
  }
 
  /**
   * When the bottom is inview port, this function is called
   * It will insert a dynamicall created NguiInviewPageComponent with the given template.
   * It will also fires (bottomInview) event, so that user can fill up items for the page.
   */
  addAnInviewPageToPages(): void {
    if (!this.isListLoading) {
      this.isListLoading = true;
 
      this.inviewPage =
        this.dynamicComponentService.createComponent(NguiInviewPageComponent, this.pagesRef);
      this.dynamicComponentService.insertComponent(this.inviewPage);
 
      this.inviewPage.instance.template = this.template;
      this.inviewPages.push(this.inviewPage);
 
      this.bottomInview.emit(this); // fire event, so that user can load items
    }
  }
 
  // set items of NguiInviewPageComponent
  addList(items: Array<any>): void {
    this.isListLoading = false;
    this.inviewPage.instance.setItems(items);
  }
 
}