Skip to content

Instantly share code, notes, and snippets.

@victor-shelepen
Created October 25, 2017 10:06
Show Gist options
  • Save victor-shelepen/6f130c53b1fd38116afe20d15a2afc40 to your computer and use it in GitHub Desktop.
Save victor-shelepen/6f130c53b1fd38116afe20d15a2afc40 to your computer and use it in GitHub Desktop.
This component displays a list of operations. It retrieves data from two observables. The data source of the table is created once. The data change is initated by Behaviour Subject dataS. Data retring process is locate in the function `update`. mat-table component uses the directive trackBy that have to optimize the output. But it still blinking.
import {Component, ViewChild, OnInit} from '@angular/core';
import {ForexService} from '../../../service/forex';
import {DataSource} from '@angular/cdk/collections';
import {Observable} from 'rxjs/Observable';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import 'rxjs/operator/map';
import {MatDialog, PageEvent} from '@angular/material';
import {MatSort} from '@angular/material';
import {IPaginator} from '../../../../core/lib/interface';
import {ConfirmDialogComponent, IConfirmDialogAnswer, IConfirmDialogData} from '../../../../core/dialog/confirm';
import * as _ from 'lodash';
@Component({
selector: 'app--forex-page--operations',
template: `
<mat-table #table [dataSource]="dataSource" matSort (matSortChange)="sortChange($event)" [trackBy]="trackById">
<ng-container cdkColumnDef="id">
<mat-header-cell *cdkHeaderCellDef> ID</mat-header-cell>
<mat-cell *cdkCellDef="let row"> {{row._id}}</mat-cell>
</ng-container>
<ng-container cdkColumnDef="type">
<mat-header-cell *cdkHeaderCellDef mat-sort-header>Type</mat-header-cell>
<mat-cell *cdkCellDef="let row">{{ row.type }}</mat-cell>
</ng-container>
<ng-container cdkColumnDef="currency">
<mat-header-cell *cdkHeaderCellDef mat-sort-header>Currency</mat-header-cell>
<mat-cell *cdkCellDef="let row">{{ row.openRate.currency.title }}</mat-cell>
</ng-container>
<ng-container cdkColumnDef="openValue">
<mat-header-cell *cdkHeaderCellDef mat-sort-header>Open value</mat-header-cell>
<mat-cell *cdkCellDef="let row">{{ row.openValue | number:'1.0' }}</mat-cell>
</ng-container>
<ng-container cdkColumnDef="outcome">
<mat-header-cell *cdkHeaderCellDef mat-sort-header>Outcome</mat-header-cell>
<mat-cell *cdkCellDef="let row">{{ row.outcome | number:'1.4-4' }}</mat-cell>
</ng-container>
<ng-container cdkColumnDef="openDate">
<mat-header-cell *cdkHeaderCellDef mat-sort-header>Opened</mat-header-cell>
<mat-cell *cdkCellDef="let row">{{ row.openDate | date:'short' }}</mat-cell>
</ng-container>
<ng-container cdkColumnDef="stopProfitValue">
<mat-header-cell *cdkHeaderCellDef mat-sort-header>Stop profit</mat-header-cell>
<mat-cell *cdkCellDef="let row">{{ row.stopProfitValue | number:'1.4-4' }}</mat-cell>
</ng-container>
<ng-container cdkColumnDef="stopLoseValue">
<mat-header-cell *cdkHeaderCellDef mat-sort-header>Stop lose</mat-header-cell>
<mat-cell *cdkCellDef="let row">{{ row.stopLoseValue | number:'1.4-4' }}</mat-cell>
</ng-container>
<ng-container cdkColumnDef="closeDate">
<mat-header-cell *cdkHeaderCellDef mat-sort-header>Closed</mat-header-cell>
<mat-cell *cdkCellDef="let row">{{ row.closeDate | date:'short' }}</mat-cell>
</ng-container>
<ng-container cdkColumnDef="closeButton">
<mat-header-cell *cdkHeaderCellDef></mat-header-cell>
<mat-cell *cdkCellDef="let row">
<button mat-menu-item *ngIf="!row.closeDate" (click)="openCloseConfirmationDialog(row._id)"><mat-icon>close</mat-icon></button>
</mat-cell>
</ng-container>
<mat-header-row *cdkHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *cdkRowDef="let row; columns: displayedColumns;" [ngClass]="{'closed': !!row.closeDate}"></mat-row>
</mat-table>
<mat-paginator
*ngIf="dataSource && dataSource.paginator"
[length]="dataSource.paginator.length"
[pageSize]="dataSource.paginator.pageSize"
[pageIndex]="dataSource.paginator.pageIndex"
[pageSizeOptions]="[5, 10, 20, 40]"
(page)="pageEvent($event)">
</mat-paginator>
`,
styles: [`
.closed {
background: lightgrey;
}
`]
})
export class OperationsComponent implements OnInit {
@ViewChild(MatSort) sort: MatSort;
dataSource: Source;
displayedColumns = ['type', 'currency', 'openValue', 'outcome', 'openDate', 'stopProfitValue', 'stopLoseValue', 'closeDate', 'closeButton'];
constructor(
private forexService: ForexService,
private dialog: MatDialog
) {
this.dataSource = new Source(this.forexService);
}
trackById(index, item) {
console.log(item._id);
return item;
//console.log(index, item);
}
ngOnInit() {
this.forexService.accountSummaryO
.subscribe((summary) => {
this.updateOperations();
});
}
updateOperations() {
this.dataSource.update();
}
sortChange(filter) {
this.dataSource.sortKey = filter.active;
this.dataSource.sortDirection = filter.direction;
this.dataSource.update();
}
pageEvent(event: PageEvent) {
this.dataSource.pageIndex = event.pageIndex;
this.dataSource.pageSize = event.pageSize;
this.dataSource.update();
}
openCloseConfirmationDialog(operationId: string) {
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
data: <IConfirmDialogData>{
title: 'Confirmation',
content: 'Confirm it to delete.',
proxyPayload: {operationId}
}
});
dialogRef.afterClosed()
.subscribe((result: IConfirmDialogAnswer) => {
if (result.isConfirmed) {
this.forexService.closeOperation(result.proxyPayload['operationId'])
.subscribe((_id: string) => {
this.updateOperations();
});
}
});
}
updateOutcome() {
this.dataSource.updateOutcome({});
}
}
export class Source extends DataSource<any> {
public paginator: IPaginator = null;
public dataS: BehaviorSubject<object[]>;
public dataO: Observable<object[]>;
outcomeSubject: BehaviorSubject<any>;
constructor(
private forexService: ForexService,
public pageIndex: number = 0,
public pageSize: number = 5,
public sortKey: string = null,
public sortDirection: string = null
) {
super();
this.dataS = new BehaviorSubject([]);
this.dataO = this.dataS.asObservable();
this.outcomeSubject = new BehaviorSubject(null);
this.forexService.getRTQuotes()
.subscribe((quotes) => {
this.updateOutcome(quotes);
});
console.log('Source constructor...');
}
public updateOutcome(quotes=null) {
if (!this.paginator || !this.paginator.array) {
return;
}
let toSend = false;
this.paginator.array.forEach((value) => {
if (value.closeDate) {
return;
}
toSend = true;
let quote = _.find(quotes, (quote) => {
return quote._id = value.openRate.currency._id;
});
value.outcome = (quote.rate.bidValue - value.openRate.askValue) * value.openValue;
});
if (toSend) {
this.outcomeSubject.next(this.paginator);
}
}
update() {
return Observable.merge(
this.forexService.getOperations(this.pageIndex, this.pageSize, this.sortKey, this.sortDirection),
// Recalculates outcome on the client side.
this.outcomeSubject.asObservable()
)
.do((paginator: IPaginator) => {
if (!paginator) {
return [];
}
this.pageIndex = paginator.pageIndex;
this.pageSize = paginator.pageSize;
this.paginator = paginator;
})
.map((paginator: IPaginator) => {
if (!paginator) {
return [];
}
return paginator.array;
})
.subscribe(this.dataS)
}
connect(): Observable<any[]> {
return this.dataO;
}
disconnect() {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment