Skip to content

Commit

Permalink
Merge pull request #2412 from cloudfoundry-incubator/table-loading-init
Browse files Browse the repository at this point in the history
Table loading init
  • Loading branch information
nwmac authored Jun 19, 2018
2 parents 3fef6ce + b67ba2c commit 7e1597b
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 103 deletions.
204 changes: 103 additions & 101 deletions src/frontend/app/shared/components/list/list.component.html
Original file line number Diff line number Diff line change
@@ -1,130 +1,132 @@
<div class="list-component" *ngIf="initialised$ | async" [ngClass]="{'list-component__table': (view$ | async) === 'table', 'list-component__cards': (view$ | async) === 'cards' }">
<mat-progress-bar color="primary" *ngIf="!(hasControls$ | async) && (showProgressBar$ | async)" mode="indeterminate"></mat-progress-bar>
<div class="list-component__header" [hidden]="!(hasControls$ | async) && ((noRowsNotFiltering$ | async) && noEntries)">
<mat-progress-bar color="primary" *ngIf="showProgressBar$ | async" mode="indeterminate"></mat-progress-bar>
<mat-card [ngClass]="{'list-component__header--selected': (dataSource.isSelecting$ | async), 'mat-header-row': (view$ | async) === 'table'}" class="list-component__header-card">
<div class="list-component__header__left">
<div class="list-component__header__left--text" *ngIf="!(isAddingOrSelecting$ | async) && config.text?.title">{{ config.text?.title }}</div>
<div class="list-component__header__left--text" *ngIf="(dataSource.isSelecting$ | async)">{{dataSource.selectedRows.size}} Selected</div>
<!-- Multi filters, such as filter app wall by cf/org/space -->
<div class="list-component__header__left--multi-filters" [hidden]="(!(hasRows$ | async) && !filter) || (isAddingOrSelecting$ | async)">
<mat-form-field *ngFor="let multiFilterConfig of multiFilterConfigs" [floatLabel]="'never'">
<mat-select matInput [(value)]="multiFilters[multiFilterConfig.key]" [disabled]="(dataSource.isLoadingPage$ | async) || (multiFilterConfig.loading$ | async) || !(multiFilterConfig.list$ | async) || !(multiFilterConfig.list$ | async).length" (selectionChange)="multiFilterConfig.select.next($event.value)">
<mat-option>All</mat-option>
<mat-option *ngFor="let selectItem of multiFilterConfig.list$ | async" [value]="selectItem.value">
{{selectItem.label}}
</mat-option>
</mat-select>
<mat-placeholder>{{multiFilterConfig.label}}</mat-placeholder>
</mat-form-field>
<app-stateful-icon *ngIf="(multiFilterConfigsLoading$ | async)" [state]="'busy'"></app-stateful-icon>
<div class="list-component" [ngClass]="{'list-component__table': (view$ | async) === 'table', 'list-component__cards': (view$ | async) === 'cards' }">
<mat-progress-bar color="primary" *ngIf="!(initialised$ | async) || (showProgressBar$ | async)" mode="indeterminate"></mat-progress-bar>
<ng-container *ngIf="initialised$ | async">
<div class="list-component__header" [hidden]="!(hasControls$ | async) && ((noRowsNotFiltering$ | async) && noEntries)">
<mat-card [ngClass]="{'list-component__header--selected': (dataSource.isSelecting$ | async), 'mat-header-row': (view$ | async) === 'table'}" class="list-component__header-card">
<div class="list-component__header__left">
<div class="list-component__header__left--text" *ngIf="!(isAddingOrSelecting$ | async) && config.text?.title">{{ config.text?.title }}</div>
<div class="list-component__header__left--text" *ngIf="(dataSource.isSelecting$ | async)">{{dataSource.selectedRows.size}} Selected</div>
<!-- Multi filters, such as filter app wall by cf/org/space -->
<div class="list-component__header__left--multi-filters" [hidden]="(!(hasRows$ | async) && !filter) || (isAddingOrSelecting$ | async)">
<mat-form-field *ngFor="let multiFilterConfig of multiFilterConfigs" [floatLabel]="'never'">
<mat-select matInput [(value)]="multiFilters[multiFilterConfig.key]" [disabled]="(dataSource.isLoadingPage$ | async) || (multiFilterConfig.loading$ | async) || !(multiFilterConfig.list$ | async) || !(multiFilterConfig.list$ | async).length" (selectionChange)="multiFilterConfig.select.next($event.value)">
<mat-option>All</mat-option>
<mat-option *ngFor="let selectItem of multiFilterConfig.list$ | async" [value]="selectItem.value">
{{selectItem.label}}
</mat-option>
</mat-select>
<mat-placeholder>{{multiFilterConfig.label}}</mat-placeholder>
</mat-form-field>
<app-stateful-icon *ngIf="(multiFilterConfigsLoading$ | async)" [state]="'busy'"></app-stateful-icon>
</div>
</div>
</div>
<div class="list-component__header__right">
<!-- Filter by text input -->
<div class="filter" [hidden]="!config.enableTextFilter || (!(hasRows$ | async) && !filter) || (dataSource.isAdding$ | async)">
<mat-form-field floatLabel="never">
<input matInput [ngModel]="filterString" #filter="ngModel" [disabled]="(dataSource.isLoadingPage$ | async)" name="filter" placeholder="{{config.text?.filter || 'Filter'}}">
</mat-form-field>
</div>
<!-- Sort Button & Drop down -->
<div class="sort" [hidden]="(view$ | async) === 'table' || sortColumns.length < 1 || (isAddingOrSelecting$ | async) || (!(dataSource.isLoadingPage$ | async) && !(hasRowsOrIsFiltering$ | async))">
<mat-form-field>
<mat-select matInput [(ngModel)]="headerSort.value" shouldPlaceholderFloat="false" [disabled]="(disableActions$ | async)" (selectionChange)="updateListSort($event.value, headerSort.direction)">
<mat-option *ngFor="let column of sortColumns" [value]="column.columnId">
{{column.headerCell()}}
</mat-option>
</mat-select>
</mat-form-field>
<button mat-icon-button (click)="updateListSort(headerSort.value, 'desc')" [disabled]="(disableActions$ | async)" *ngIf="headerSort.direction==='asc'">
<div class="list-component__header__right">
<!-- Filter by text input -->
<div class="filter" [hidden]="!config.enableTextFilter || (!(hasRows$ | async) && !filter) || (dataSource.isAdding$ | async)">
<mat-form-field floatLabel="never">
<input matInput [ngModel]="filterString" #filter="ngModel" [disabled]="(dataSource.isLoadingPage$ | async)" name="filter" placeholder="{{config.text?.filter || 'Filter'}}">
</mat-form-field>
</div>
<!-- Sort Button & Drop down -->
<div class="sort" [hidden]="(view$ | async) === 'table' || sortColumns.length < 1 || (isAddingOrSelecting$ | async) || (!(dataSource.isLoadingPage$ | async) && !(hasRowsOrIsFiltering$ | async))">
<mat-form-field>
<mat-select matInput [(ngModel)]="headerSort.value" shouldPlaceholderFloat="false" [disabled]="(disableActions$ | async)" (selectionChange)="updateListSort($event.value, headerSort.direction)">
<mat-option *ngFor="let column of sortColumns" [value]="column.columnId">
{{column.headerCell()}}
</mat-option>
</mat-select>
</mat-form-field>
<button mat-icon-button (click)="updateListSort(headerSort.value, 'desc')" [disabled]="(disableActions$ | async)" *ngIf="headerSort.direction==='asc'">
<mat-icon>sort</mat-icon>
</button>
<button mat-icon-button (click)="updateListSort(headerSort.value, 'asc')" [disabled]="(disableActions$ | async)" *ngIf="headerSort.direction==='desc'">
<button mat-icon-button (click)="updateListSort(headerSort.value, 'asc')" [disabled]="(disableActions$ | async)" *ngIf="headerSort.direction==='desc'">
<mat-icon style="transform: rotate(180deg);">sort</mat-icon>
</button>
</div>
<!-- Multi actions (those applied to selection) -->
<div *ngIf="(dataSource.isSelecting$ | async)">
<ng-container *ngFor="let action of multiActions">
<button mat-icon-button *ngIf="action.visible$ | async" (click)="executeActionMultiple(action)" [disabled]="!(action.enabled$ | async)" matTooltip="{{action.description}}" matTooltipShowDelay="1000">
</div>
<!-- Multi actions (those applied to selection) -->
<div *ngIf="(dataSource.isSelecting$ | async)">
<ng-container *ngFor="let action of multiActions">
<button mat-icon-button *ngIf="action.visible$ | async" (click)="executeActionMultiple(action)" [disabled]="!(action.enabled$ | async)" matTooltip="{{action.description}}" matTooltipShowDelay="1000">
<mat-icon>{{action.icon}}</mat-icon>
</button>
</ng-container>
</div>
<!-- Global actions (those not applied to specific rows) -->
<div *ngIf="!(isAddingOrSelecting$ | async) && globalActions?.length > 0">
<ng-container *ngFor="let action of globalActions">
<button mat-icon-button *ngIf="action.visible$ | async" (click)="executeActionGlobal(action)" [disabled]="!(action.enabled$ | async)" matTooltip="{{action.description}}" matTooltipShowDelay="1000">
</ng-container>
</div>
<!-- Global actions (those not applied to specific rows) -->
<div *ngIf="!(isAddingOrSelecting$ | async) && globalActions?.length > 0">
<ng-container *ngFor="let action of globalActions">
<button mat-icon-button *ngIf="action.visible$ | async" (click)="executeActionGlobal(action)" [disabled]="!(action.enabled$ | async)" matTooltip="{{action.description}}" matTooltipShowDelay="1000">
<mat-icon>{{action.icon}}</mat-icon>
</button>
</ng-container>
</div>
<!-- Add new row form -->
<div *ngIf="addForm && (dataSource.isAdding$ | async) && !(dataSource.isSelecting$ | async)" class="add-container">
<div class="spacer"></div>
<ng-content select="[app-table-add]"></ng-content>
<button mat-icon-button (click)="dataSource.saveAdd()" [disabled]="!safeAddForm().valid">
</ng-container>
</div>
<!-- Add new row form -->
<div *ngIf="addForm && (dataSource.isAdding$ | async) && !(dataSource.isSelecting$ | async)" class="add-container">
<div class="spacer"></div>
<ng-content select="[app-table-add]"></ng-content>
<button mat-icon-button (click)="dataSource.saveAdd()" [disabled]="!safeAddForm().valid">
<mat-icon>done</mat-icon>
</button>
<button mat-icon-button (click)="dataSource.cancelAdd()">
<button mat-icon-button (click)="dataSource.cancelAdd()">
<mat-icon>clear</mat-icon>
</button>
</div>
<!-- Add form button -->
<div *ngIf="addForm && !(isAddingOrSelecting$ | async)">
<button mat-icon-button [disabled]="(dataSource.isLoadingPage$ | async)" (click)="safeAddForm().reset();dataSource.startAdd();">
</div>
<!-- Add form button -->
<div *ngIf="addForm && !(isAddingOrSelecting$ | async)">
<button mat-icon-button [disabled]="(dataSource.isLoadingPage$ | async)" (click)="safeAddForm().reset();dataSource.startAdd();">
<mat-icon>add</mat-icon>
</button>
</div>
<!-- Select table or cards view button -->
<div *ngIf="config.viewType === 'both' && !(isAddingOrSelecting$ | async) && ((dataSource.isLoadingPage$ | async) || (hasRowsOrIsFiltering$ | async))">
<button mat-icon-button [disabled]="(dataSource.isLoadingPage$ | async)" (click)="updateListView('cards');" *ngIf="(view$ | async)==='table'">
</div>
<!-- Select table or cards view button -->
<div *ngIf="config.viewType === 'both' && !(isAddingOrSelecting$ | async) && ((dataSource.isLoadingPage$ | async) || (hasRowsOrIsFiltering$ | async))">
<button mat-icon-button [disabled]="(dataSource.isLoadingPage$ | async)" (click)="updateListView('cards');" *ngIf="(view$ | async)==='table'">
<mat-icon>grid_on</mat-icon>
</button>
<button mat-icon-button [disabled]="(dataSource.isLoadingPage$ | async)" (click)="updateListView('table');" *ngIf="(view$ | async)==='cards'">
<button mat-icon-button [disabled]="(dataSource.isLoadingPage$ | async)" (click)="updateListView('table');" *ngIf="(view$ | async)==='cards'">
<mat-icon>list</mat-icon>
</button>
</div>
<button mat-icon-button *ngIf="dataSource.refresh && !(isAddingOrSelecting$ | async)" [disabled]="dataSource.isLoadingPage$ | async" (click)="refresh()">
</div>
<button mat-icon-button *ngIf="dataSource.refresh && !(isAddingOrSelecting$ | async)" [disabled]="dataSource.isLoadingPage$ | async" (click)="refresh()">
<mat-icon class="refresh-icon" [ngClass]="{refreshing: (isRefreshing$ | async)}" aria-label="Refresh list data">refresh</mat-icon>
</button>
</div>
</mat-card>
</div>
<div class="refresh-button__no-header" *ngIf="dataSource.refresh && !(hasControls$ | async) && (!(hasRowsOrIsFiltering$ | async) && noEntries)">
<button mat-mini-fab *ngIf="!(isAddingOrSelecting$ | async)" [disabled]="dataSource.isLoadingPage$ | async" (click)="refresh()">
</div>

</mat-card>
</div>
<div class="refresh-button__no-header" *ngIf="dataSource.refresh && !(hasControls$ | async) && (!(hasRowsOrIsFiltering$ | async) && noEntries)">
<button mat-mini-fab *ngIf="!(isAddingOrSelecting$ | async)" [disabled]="dataSource.isLoadingPage$ | async" (click)="refresh()">
<mat-icon class="refresh-icon" [ngClass]="{refreshing: (isRefreshing$ | async)}" aria-label="Refresh list data">refresh</mat-icon>
</button>
</div>
<div class="list-component__body">
<div class="list-component__body-inner" [hidden]="!(hasRows$ | async)" [@list]="(pageState$ | async)">
<app-cards *ngIf="(view$ | async) === 'cards'" #cards [dataSource]="dataSource" [component]="config.cardComponent" [hidden]="!(hasRows$ | async)"></app-cards>
<app-table *ngIf="(view$ | async) === 'table'" #table [dataSource]="dataSource" [addActions]="(config.getSingleActions() || []).length > 0" [addSelect]="config.allowSelection || (haveMultiActions | async)" [paginationController]="paginationController"
[columns]="columns" [fixedRowHeight]="config.tableFixedRowHeight" [hidden]="!(hasRows$ | async)">
</app-table>
</div>
<mat-card class="list-component__paginator" [hidden]="(hidePaginator$ | async)">
<mat-paginator [pageSizeOptions]="paginatorSettings.pageSizeOptions" [pageSize]="paginatorSettings.pageSize" [pageIndex]="paginatorSettings.pageIndex" showFirstLastButtons="true" [length]="paginatorSettings.length">
</mat-paginator>
</mat-card>
</div>
<ng-template #defaultNoEntries>
<mat-card class="list-component__default-no-entries">
<mat-card-content>
<div class="no-rows">{{config.text?.noEntries || 'There are no entries.'}}</div>
</mat-card-content>
</mat-card>
</ng-template>
<div [hidden]="(showProgressBar$ | async) || (hasRows$ | async)" class="list-component__no-entries">
<ng-container *ngIf="(noRowsNotFiltering$ | async)">
<ng-container *ngTemplateOutlet="noEntries ? noEntries : defaultNoEntries">
<div class="list-component__body">
<div class="list-component__body-inner" [hidden]="!(hasRows$ | async)" [@list]="(pageState$ | async)">
<app-cards *ngIf="(view$ | async) === 'cards'" #cards [dataSource]="dataSource" [component]="config.cardComponent" [hidden]="!(hasRows$ | async)"></app-cards>
<app-table *ngIf="(view$ | async) === 'table'" #table [dataSource]="dataSource" [addActions]="(config.getSingleActions() || []).length > 0" [addSelect]="config.allowSelection || (haveMultiActions | async)" [paginationController]="paginationController"
[columns]="columns" [fixedRowHeight]="config.tableFixedRowHeight" [hidden]="!(hasRows$ | async)">
</app-table>
</div>
<mat-card class="list-component__paginator" [hidden]="(hidePaginator$ | async)">
<mat-paginator [pageSizeOptions]="paginatorSettings.pageSizeOptions" [pageSize]="paginatorSettings.pageSize" [pageIndex]="paginatorSettings.pageIndex" showFirstLastButtons="true" [length]="paginatorSettings.length">
</mat-paginator>
</mat-card>
</div>
<ng-template #defaultNoEntries>
<mat-card class="list-component__default-no-entries">
<mat-card-content>
<div class="no-rows">{{config.text?.noEntries || 'There are no entries.'}}</div>
</mat-card-content>
</mat-card>
</ng-template>
<div [hidden]="(showProgressBar$ | async) || (hasRows$ | async)" class="list-component__no-entries">
<ng-container *ngIf="(noRowsNotFiltering$ | async)">
<ng-container *ngTemplateOutlet="noEntries ? noEntries : defaultNoEntries">
</ng-container>
</ng-container>
</ng-container>
<ng-container *ngIf="(noRowsHaveFilter$ | async)">
<ng-container *ngTemplateOutlet="noEntriesForCurrentFilter ? noEntriesForCurrentFilter : defaultNoEntries">
<ng-container *ngIf="(noRowsHaveFilter$ | async)">
<ng-container *ngTemplateOutlet="noEntriesForCurrentFilter ? noEntriesForCurrentFilter : defaultNoEntries">
</ng-container>
</ng-container>
</ng-container>
</div>
</div>
</ng-container>
</div>

<ng-template #refreshButton>
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/app/shared/components/list/list.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
takeWhile,
tap,
withLatestFrom,
switchMap,
} from 'rxjs/operators';
import { animate, style, transition, trigger } from '@angular/animations';
import {
Expand Down Expand Up @@ -430,8 +431,7 @@ export class ListComponent<T> implements OnInit, OnDestroy, AfterViewInit {

const canShowLoading$ = this.dataSource.isLoadingPage$.pipe(
distinctUntilChanged((previousVal, newVal) => !previousVal && newVal),
withLatestFrom(this.dataSource.pagination$),
map(([loading, page]) => page),
switchMap(() => this.dataSource.pagination$),
map(pag => pag.currentPage),
pairwise(),
map(([oldPage, newPage]) => oldPage !== newPage),
Expand Down

0 comments on commit 7e1597b

Please sign in to comment.