libs/depiction/src/lib/dataset-table/dataset-table.component.ts
selector | n52-dataset-table |
styleUrls | ./dataset-table.component.scss |
templateUrl | ./dataset-table.component.html |
constructor(iterableDiffers: IterableDiffers, servicesConnector: HelgolandServicesConnector, datasetIdResolver: InternalIdHandler, timeSrvc: Time, translateSrvc: TranslateService, timezoneSrvc: TimezoneService)
|
|||||||||||||||||||||
Parameters :
|
datasetIds | |
Type : string[]
|
|
Default value : []
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:38
|
|
List of presented dataset ids. |
datasetOptions | |
Type : Map<string | T>
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:56
|
|
The corresponding dataset options. |
presenterOptions | |
Type : U
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:63
|
|
Options for general presentation of the data. |
reloadForDatasets | |
Type : string[]
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:70
|
|
List of datasets for which a reload should be triggered, when the Array is set to new value. |
selectedDatasetIds | |
Type : string[]
|
|
Default value : []
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:44
|
|
List of presented selected dataset ids. |
timeInterval | |
Type : TimeInterval
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:50
|
|
The time interval in which the data should presented. |
dataLoaded | |
Type : EventEmitter<Set<string>>
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:100
|
|
Event, which triggers list of datasets where data is currently loaded. |
onContentLoading | |
Type : EventEmitter<boolean>
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:94
|
|
Event flag, while there is data loaded in the component. |
onDatasetSelected | |
Type : EventEmitter<string[]>
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:76
|
|
Event with a list of selected datasets. |
onMessageThrown | |
Type : EventEmitter<PresenterMessage>
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:88
|
|
Event, when there occured a message in the component. |
onTimespanChanged | |
Type : EventEmitter<Timespan>
|
|
Inherited from
DatasetPresenterComponent
|
|
Defined in
DatasetPresenterComponent:82
|
|
Event when the timespan in the presentation is adjusted. |
window:resize |
Arguments : '$event'
|
window:resize(event: Event)
|
Inherited from
ResizableComponent
|
Defined in
ResizableComponent:6
|
Protected addDataset |
addDataset(id: string, url: string)
|
Returns :
void
|
Private addTimeseries | ||||||
addTimeseries(timeseries: HelgolandTimeseries)
|
||||||
Parameters :
Returns :
void
|
Protected datasetOptionsChanged | |||||||||
datasetOptionsChanged(internalId: string, options: DatasetOptions)
|
|||||||||
Parameters :
Returns :
void
|
Protected getIndexFromInternalId | ||||||
getIndexFromInternalId(internalId: string)
|
||||||
Parameters :
Returns :
any
|
Private loadTsData | ||||||
loadTsData(timeseries: HelgolandTimeseries)
|
||||||
Parameters :
Returns :
void
|
Public ngOnInit |
ngOnInit()
|
Returns :
void
|
Protected onLanguageChanged | ||||||
onLanguageChanged(langChangeEvent: LangChangeEvent)
|
||||||
Parameters :
Returns :
void
|
Protected onResize |
onResize()
|
Returns :
void
|
Protected onTimezoneChanged | ||||||
onTimezoneChanged(timezone: string)
|
||||||
Parameters :
Returns :
void
|
Private prepareData | |||||||||
prepareData(timeseries: HelgolandTimeseries, newdata: DatasetTableData[])
|
|||||||||
Parameters :
Returns :
void
|
Protected presenterOptionsChanged | ||||||
presenterOptionsChanged(options: any)
|
||||||
Parameters :
Returns :
void
|
Public reloadDataForDatasets | ||||||
reloadDataForDatasets(datasetIds: string[])
|
||||||
Parameters :
Returns :
void
|
Protected removeDataset | ||||||
removeDataset(internalId: string)
|
||||||
Parameters :
Returns :
void
|
Protected removeSelectedId | ||||||
removeSelectedId(internalId: string)
|
||||||
Parameters :
Returns :
void
|
Protected setSelectedId | ||||||
setSelectedId(internalId: string)
|
||||||
Parameters :
Returns :
void
|
Public sort | ||||||
sort(event: any)
|
||||||
Parameters :
Returns :
void
|
Protected timeIntervalChanges |
timeIntervalChanges()
|
Returns :
void
|
Protected Abstract addDataset |
addDataset(id: string, url: string)
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:191
|
Returns :
void
|
Protected addDatasetByInternalId | ||||||
addDatasetByInternalId(internalId: string)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:180
|
||||||
Parameters :
Returns :
void
|
Protected Abstract datasetOptionsChanged |
datasetOptionsChanged(internalId: string, options: T, firstChange: boolean)
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:201
|
Returns :
void
|
Public ngDoCheck |
ngDoCheck()
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:139
|
Returns :
void
|
Public ngOnChanges | ||||||
ngOnChanges(changes: SimpleChanges)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:124
|
||||||
Parameters :
Returns :
void
|
Public ngOnDestroy |
ngOnDestroy()
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:134
|
Returns :
void
|
Protected Abstract onLanguageChanged | ||||||
onLanguageChanged(langChangeEvent: LangChangeEvent)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:185
|
||||||
Parameters :
Returns :
void
|
Protected Abstract onTimezoneChanged | ||||||
onTimezoneChanged(timezone: string)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:187
|
||||||
Parameters :
Returns :
void
|
Protected Abstract presenterOptionsChanged | ||||||
presenterOptionsChanged(options: U)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:199
|
||||||
Parameters :
Returns :
void
|
Public Abstract reloadDataForDatasets | ||||||
reloadDataForDatasets(datasets: string[])
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:178
|
||||||
Parameters :
Returns :
void
|
Protected Abstract removeDataset | ||||||
removeDataset(internalId: string)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:193
|
||||||
Parameters :
Returns :
void
|
Protected Abstract removeSelectedId | ||||||
removeSelectedId(internalId: string)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:197
|
||||||
Parameters :
Returns :
void
|
Protected Abstract setSelectedId | ||||||
setSelectedId(internalId: string)
|
||||||
Inherited from
DatasetPresenterComponent
|
||||||
Defined in
DatasetPresenterComponent:195
|
||||||
Parameters :
Returns :
void
|
Protected Abstract timeIntervalChanges |
timeIntervalChanges()
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:189
|
Returns :
void
|
Protected Abstract onResize |
onResize()
|
Inherited from
ResizableComponent
|
Defined in
ResizableComponent:10
|
Returns :
void
|
Private additionalStylesheet |
Type : HTMLElement
|
Public preparedColors |
Type : string[]
|
Default value : Array()
|
Public preparedData |
Type : DatasetTableData[]
|
Default value : Array()
|
Public ready |
Default value : false
|
Public timeseriesArray |
Type : HelgolandTimeseries[]
|
Default value : new Array()
|
Private datasetIdsDiffer |
Type : IterableDiffer<string>
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:104
|
Private langChangeSubscription |
Type : Subscription
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:106
|
Protected oldDatasetOptions |
Type : Map<string | T>
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:57
|
Protected oldPresenterOptions |
Type : U
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:64
|
Private selectedDatasetIdsDiffer |
Type : IterableDiffer<string>
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:105
|
Protected timespan |
Type : Timespan
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:102
|
Private timezoneSubscription |
Type : Subscription
|
Inherited from
DatasetPresenterComponent
|
Defined in
DatasetPresenterComponent:107
|
import { Component, IterableDiffers, OnInit } from '@angular/core';
import {
DatasetOptions,
DatasetPresenterComponent,
DatasetTableData,
DatasetType,
HelgolandServicesConnector,
HelgolandTimeseries,
HelgolandTimeseriesData,
InternalIdHandler,
Time,
TimezoneService,
} from '@helgoland/core';
import { LangChangeEvent, TranslateService } from '@ngx-translate/core';
@Component({
selector: 'n52-dataset-table',
templateUrl: './dataset-table.component.html',
styleUrls: ['./dataset-table.component.scss']
})
export class DatasetTableComponent extends DatasetPresenterComponent<DatasetOptions, any> implements OnInit {
/*
The component extends DatasetGraphComponent, but implements only parts of that components inputs and outputs.
Implemented: datasetIds, timeInterval, selectedDatasetIds and datasetOptions inputs; no outputs
Not implemented: graphOptions input; all outputs (onDatasetSelected, onTimespanChanged, onMessageThrown, onLoading)
*/
public preparedData: DatasetTableData[] = Array();
public preparedColors: string[] = Array();
public ready = false;
public timeseriesArray: HelgolandTimeseries[] = new Array();
private additionalStylesheet: HTMLElement;
constructor(
protected iterableDiffers: IterableDiffers,
protected servicesConnector: HelgolandServicesConnector,
protected datasetIdResolver: InternalIdHandler,
protected timeSrvc: Time,
protected translateSrvc: TranslateService,
protected timezoneSrvc: TimezoneService
) {
super(iterableDiffers, servicesConnector, datasetIdResolver, timeSrvc, translateSrvc, timezoneSrvc);
}
public ngOnInit() {
this.additionalStylesheet = document.getElementById('selectedIdsStylesheet');
if (!this.additionalStylesheet) {
this.additionalStylesheet = document.createElement('style');
this.additionalStylesheet.id = 'selectedIdsStylesheet';
document.body.appendChild(this.additionalStylesheet);
}
}
/* called when user clicks on table headers */
public sort(event: any) {
// can be 'datetime' or an integer indicating the index of the column in the values array
const by = event.target.dataset.columnId;
const direction = event.target.classList.contains('sorted-asc') ? 'desc' : 'asc';
const directionNumber = (direction === 'asc' ? 1 : -1);
// set CSS classes
Array.from(event.target.parentElement.children).forEach((child: Element) => child.className = '');
if (direction === 'asc') {
(event.target as Element).classList.add('sorted-asc');
} else {
(event.target as Element).classList.add('sorted-desc');
}
// define correct callback function for sort method
let sortCallback;
if (by === 'datetime') {
sortCallback = (e1: any, e2: any) => directionNumber * (e1.datetime - e2.datetime);
} else {
const index = parseInt(by, 10);
// basically the same as above, but take care of 'undefined' values
sortCallback = (e1: any, e2: any) =>
(e1.values[index] === undefined ? 1 :
(e2.values[index] === undefined ? -1 :
(directionNumber * (e1.values[index] - e2.values[index]))
)
);
}
// do the sort
this.preparedData = this.preparedData.sort(sortCallback);
}
protected onLanguageChanged(langChangeEvent: LangChangeEvent): void { }
protected onTimezoneChanged(timezone: string): void { }
public reloadDataForDatasets(datasetIds: string[]): void {
// console.log('reload data at ' + new Date());
}
protected presenterOptionsChanged(options: any) {
// only included because it's required by abstract parent class (wouldn't compile without)
// no point in implementing this method in a non-graphing component
}
protected getIndexFromInternalId(internalId: string) {
// helper method
return this.datasetIds.indexOf(internalId);
}
protected setSelectedId(internalId: string) {
// quite fairly tested
const rules = this.additionalStylesheet.innerHTML.split('\r\n');
const index = this.getIndexFromInternalId(internalId);
rules[index] = 'td:nth-child(' + (index + 2) + ') {font-weight: bold}';
this.additionalStylesheet.innerHTML = rules.join('\r\n');
}
protected removeSelectedId(internalId: string) {
// fairly tested
const rules = this.additionalStylesheet.innerHTML.split('\r\n');
const index = this.getIndexFromInternalId(internalId);
rules[index] = '';
this.additionalStylesheet.innerHTML = rules.join('\r\n');
}
protected timeIntervalChanges() {
// the easiest method: delete everything and build preparedData from scratch.
this.preparedData = [];
this.timeseriesArray.forEach((timeseries) => this.loadTsData(timeseries));
}
protected removeDataset(internalId: string) {
// fairly tested
const index = this.getIndexFromInternalId(internalId);
// remove entries of this dataset in each datetime's `values` arrays
this.preparedData.forEach((e) => e.values.splice(index, 1));
// if a datetime became completely empty (i.e. there's only `undefined`s in the `values` array, delete this datetime)
this.preparedData = this.preparedData.filter((e) => e.values.reduce((a, c) => a || c, undefined) !== undefined);
this.preparedColors.splice(index, 1);
const rules = this.additionalStylesheet.innerHTML.split('\r\n');
rules.splice(index, 1);
this.additionalStylesheet.innerHTML = rules.join('\r\n');
this.timeseriesArray.splice(index, 1);
}
protected addDataset(id: string, url: string): void {
this.timeseriesArray.length += 1; // create new empty slot
this.preparedColors.push('darkgrey');
this.additionalStylesheet.innerHTML += '\r\n';
this.servicesConnector.getDataset({ id, url }, { type: DatasetType.Timeseries })
.subscribe(ds => this.addTimeseries(ds));
}
protected datasetOptionsChanged(internalId: string, options: DatasetOptions): void {
if (this.timeseriesArray.some((e) => e !== undefined && e.internalId === internalId)) {
const index = this.getIndexFromInternalId(internalId);
this.preparedColors[index] = options.color;
// TODO-CF: Page isn't refreshed instantly, but only after the next sort (or possible other actions as well)
}
}
protected onResize(): void {
// TODO-CF: needed???? probably not
}
private addTimeseries(timeseries: HelgolandTimeseries) {
this.timeseriesArray[this.getIndexFromInternalId(timeseries.internalId)] = timeseries;
this.loadTsData(timeseries);
}
private loadTsData(timeseries: HelgolandTimeseries) {
if (this.timespan) {
// const datasetOptions = this.datasetOptions.get(timeseries.internalId);
this.servicesConnector.getDatasetData(timeseries, this.timespan).subscribe(
result => {
// bring result into Array<DatasetTableData> format and pass to prepareData
// convention for layout of newdata argument: see 3-line-comment in prepareData function
if (result instanceof HelgolandTimeseriesData) {
const index = this.getIndexFromInternalId(timeseries.internalId);
this.prepareData(timeseries, result.values.map((e) => {
const a = new Array(this.datasetIds.length).fill(undefined);
a[index] = e[1];
return { datetime: e[0], values: a };
}));
}
}
);
}
}
private prepareData(timeseries: HelgolandTimeseries, newdata: DatasetTableData[]) {
const index = this.getIndexFromInternalId(timeseries.internalId);
// if datasetOptions are provided, use their color to style the header's "color band" (i.e. the 7px border-bottom of th)
if (this.datasetOptions) {
const datasetOptions = this.datasetOptions.get(timeseries.internalId);
this.preparedColors[index] = datasetOptions.color;
} else {
// when no color is specified: make border transparent so the header's background color is used for the color band, too
this.preparedColors[index] = 'rgba(0,0,0,0)';
}
if (this.selectedDatasetIds.indexOf(timeseries.internalId) !== -1) {
this.setSelectedId(timeseries.internalId);
}
// `newdata` is expected in exactly the same format `preparedData` would look like if that timeseries was the only one
// to actually have data (i.e. `values` has the length of timeseriesArray, but all slots are `undefined`, except for
// the slot that corresponds to that timeseries)
// `timeseries` is first timeseries added -> no other `preparedData` to merge with
if (this.preparedData.length === 0) {
// set newdata as preparedData (as per above)
this.preparedData = newdata;
// `timeseries` is not the first timeseries added -> we have to merge `newdata` into the existing `preparedData`
} else {
let i = 0; // loop variable for `preparedData`
let j = 0; // loop variable for `newdata`
// go through all data points in `newdata`
while (j < newdata.length) {
// timestamps match
if (this.preparedData[i] && this.preparedData[i].datetime === newdata[j].datetime) {
// just add `newdata`'s value to the existing `values` array in `preparedData`
this.preparedData[i].values[index] = newdata[j].values[index];
// increment both
i++;
j++;
// `newdata` is ahead of `preparedData`
} else if (this.preparedData[i] && this.preparedData[i].datetime < newdata[j].datetime) {
// do nothing because there's already an undefined there
// give preparedData the chance to catch up with newdata
i++;
// `preparedData` is ahead of `newdata`
} else {
// the current `newdata` is the first dataset that has this datetime -> add it to the preparedData array
this.preparedData.splice(i, 0, newdata[j]);
// give newdata the chance to catch up with preparedData
j++;
// but preparedData is 1 longer now, too
i++;
}
}
}
this.ready = this.timeseriesArray.every((e) => e !== undefined);
}
}
<table *ngIf="ready">
<thead>
<tr>
<th (click)="sort($event)" [attr.data-column-id]="'datetime'" class="sorted-asc">
Zeit
</th>
<th *ngFor="let series of this.timeseriesArray; let i = index" (click)="sort($event)" [attr.data-column-id]="i" [ngStyle]="{ 'border-color': preparedColors[i] }">
{{series?.label}} [{{series?.uom}}]
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of this.preparedData">
<td>{{row.datetime | tzDate: 'L LT z'}}</td>
<td *ngFor="let value of row.values">{{value}}</td>
</tr>
</tbody>
</table>
./dataset-table.component.scss
:host {
flex: 1;
overflow-y: scroll; /* make table scrollable */
overflow-x: hidden; /* suppress horizontal scrollbar */
/* Start fixed table header: http://jsfiddle.net/yeAhU/261/ */
tbody, thead tr {
display: table;
table-layout: fixed;
width: 100%;
}
table {
display: block;
border-collapse: separate;
border-spacing: 0 1px;
}
thead {
display:block;
position: sticky;
top: 0;
border-spacing: 0;
}
/* End fixed table header */
tr {
/* zebra stripes */
&:nth-child(2n) {
background-color: #eee;
}
}
th {
/* darken table head */
background-color: darkgray;
cursor: pointer;
border-bottom-width: 7px;
border-bottom-style: solid;
/* if word doesn't fit on whole line, breaks the word after any character without inserting a hyphen or anything else */
overflow-wrap: break-word;
/* if word doesn't fit on the rest of the current line, breaks the word where the browser's algorithm thinks it's approriate and renders a hyphen */
/*hyphens: auto;*/
&:first-child {
border-bottom-color: darkgray;
&.sorted-asc, &.sorted-desc {
border-bottom-color: #555;
}
}
/* highlight sorted column */
&.sorted-asc, &.sorted-desc {
background-color: #555;
color: white;
}
/* Display "up" and "down" triangles accordingly */
&.sorted-asc:after {
content: "\25B4"; /* up triangle */
float: right;
}
&.sorted-desc:after {
content: "\25BE"; /* down triangle */
float: right;
}
}
td {
/* prevents datetime from wrapping in small windows or when there's many columns */
white-space: nowrap;
/* thin horizontal lines between rows */
border-bottom: 1px solid gray;
}
th, td {
padding: 5px 10px;
}
}