Eigenständige Chart-Komponente

This commit is contained in:
Tobias Peper 2025-04-11 19:53:57 +02:00
parent 9d790cf286
commit 780f3cb6a5
8 changed files with 184 additions and 72 deletions

View File

@ -1,14 +1,32 @@
<main class="main">
<div ngbDropdown class="d-inline-block" #houseDrop="ngbDropdown">
Haushalt:
<button type="button" class="btn btn-outline-primary" id="dropdownBasic1" ngbDropdownToggle>
{{getHouseName()}}
</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<button ngbDropdownItem (click)="$event.stopPropagation(); houseId=1;">Kerpen</button>
<button ngbDropdownItem (click)="$event.stopPropagation(); houseId=2;">Rommerskirchen</button>
</div>
</div>
<div ngbDropdown class="d-inline-block">
<button type="button" class="btn btn-outline-primary" id="dropdownBasic2" ngbDropdownToggle>
Zeitraum
</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic2">
<button ngbDropdownItem (click)="$event.stopPropagation(); duration=120;">letzte 2 Stunden</button>
<button ngbDropdownItem (click)="$event.stopPropagation(); duration=360;">letzte 6 Stunden</button>
<button ngbDropdownItem (click)="$event.stopPropagation(); duration=1440;">letzte 24 Stunden</button>
</div>
</div>
<div ngbAccordion>
<div ngbAccordionItem>
<h2 ngbAccordionHeader>
<button ngbAccordionButton>Diagramm der letzten 24h</button>
<button ngbAccordionButton>Diagramm</button>
</h2>
<div ngbAccordionCollapse>
<div ngbAccordionBody>
<div style="border: 1px solid blueviolet;width: 100%; height: 100%;">
<canvas id="canvas"></canvas>
</div>
<app-chart houseId="{{houseId}}" duration="{{duration}}"></app-chart>
</div>
</div>
</div>
@ -18,7 +36,7 @@
</h2>
<div ngbAccordionCollapse>
<div ngbAccordionBody>
<ng-template><app-test duration="P1D"></app-test></ng-template>
<ng-template><app-test duration="P1D" houseId="{{houseId}}"></app-test></ng-template>
</div>
</div>
</div>

View File

@ -1,77 +1,44 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { TestComponent } from "./test/test.component";
import { ChartComponent } from './chart/chart.component';
import { RestService } from './rest.service';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { Chart } from 'chart.js/auto';
import 'chartjs-adapter-date-fns';
import { de } from 'date-fns/locale';
import { Aggregate } from './aggregate';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, TestComponent, NgbModule,],
imports: [RouterOutlet, TestComponent, ChartComponent, NgbModule,NgbDropdownModule,],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent implements OnInit {
export class AppComponent implements OnInit, OnChanges {
constructor(private rest: RestService) {
}
ngOnChanges(changes: SimpleChanges): void {
}
ngOnInit(): void {
this.rest.getLatestData().subscribe((data: Aggregate[]) => {
this.chart = new Chart('canvas', {
type: 'line',
options: {
scales: {
x: {
type: 'time',
time: {
tooltipFormat: 'HH:mm',
displayFormats: {
hour: 'HH:mm',
minute: 'HH:mm'
}
},
title: {
display: true,
text: 'Zeitpunkt'
},
adapters: {
date: {
locale: de
}
}
},
y: {
type: 'linear'
//stacked: true,
}
}
},
data: {
labels: data.map(row => row.timestampStart),
datasets: [
{
label: 'Bezug',
pointRadius: 0,
data: data.map(row => row.obtainedEnergy/60000)
}, {
label: 'Erzeugt',
pointRadius: 0,
data: data.map(row => row.producedEnergy/60000)
}, {
label: 'Eingespeist',
pointRadius: 0,
data: data.map(row => row.injectedEnergy/60000)
}
]
}
})
})
this.houseId = 1;
this.duration = 120;
}
title = 'test1';
chart: Chart | null = null;
getHouseName(): string {
if (this.houseId) {
if (this.houseId === 1) {
return 'Kerpen';
} else if (this.houseId === 2) {
return 'Rommerskirchen';
} else {
return 'Unbekannt';
}
} else {
return 'Unbekannt';
}
}
houseId: number | undefined = undefined;
duration: number | undefined = undefined;
}

View File

View File

@ -0,0 +1,3 @@
<div style="border: 1px solid blueviolet;width: 50%; height: 50%;">
<canvas id="canvas"></canvas>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ChartComponent } from './chart.component';
describe('ChartComponent', () => {
let component: ChartComponent;
let fixture: ComponentFixture<ChartComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ChartComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ChartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,93 @@
import { Component, OnInit, OnChanges, SimpleChanges, numberAttribute, Input } from '@angular/core';
import { Chart } from 'chart.js/auto';
import 'chartjs-adapter-date-fns';
import { de } from 'date-fns/locale';
import { RestService } from '../rest.service';
import { Aggregate } from '../aggregate';
@Component({
selector: 'app-chart',
standalone: true,
imports: [],
templateUrl: './chart.component.html',
styleUrl: './chart.component.css'
})
export class ChartComponent implements OnInit, OnChanges {
@Input({ transform: numberAttribute }) houseId!: number;
@Input({ transform: numberAttribute }) duration!: number;
chart: Chart | null = null;
constructor(private rest: RestService) {
}
ngOnInit(): void {
console.log('Nothing yet on chart.ngOnInit()');
}
ngOnChanges(changes: SimpleChanges): void {
this.rest.getLatestData(this.houseId, this.duration).subscribe((data: Aggregate[]) => {
console.log('Change chart');
if (this.chart) {
this.chart.clear();
this.chart.destroy();
}
this.chart = new Chart('canvas', {
type: 'line',
options: {
scales: {
x: {
type: 'time',
time: {
tooltipFormat: 'HH:mm',
displayFormats: {
hour: 'HH:mm',
minute: 'HH:mm'
}
},
title: {
display: true,
text: 'Zeitpunkt'
},
adapters: {
date: {
locale: de
}
}
},
y: {
type: 'linear',
//stacked: true,
title: {
display: true,
text: 'Leistung [Watt]'
},
}
}
},
data: {
labels: data.map(row => row.timestampStart),
datasets: [
{
label: 'Bezug (Stromnetz)',
borderColor: '#ff0000',
pointRadius: 1,
data: data.map(row => row.obtainedEnergy/60000)
}, {
label: 'Erzeugt (Solar)',
borderColor: '#ffa500',
pointRadius: 1,
data: data.map(row => row.producedEnergy/60000)
}, {
label: 'Eingespeist (Stromnetz)',
borderColor: '#006400',
pointRadius: 1,
data: data.map(row => row.injectedEnergy/60000)
}
]
}
});
});
}
}

View File

@ -16,15 +16,15 @@ export class RestService {
return this.http.get<Statistics>('https://vz.home.peper.info/rest-vz/stats?houseId=1&duration=P7D');
}
getLatestData(): Observable<Aggregate[]> {
return this.http.get<Aggregate[]>('https://vz.home.peper.info/rest-vz/latest-data?houseId=2&timeWindow=1440');
getLatestData(houseId: Number, duration: Number): Observable<Aggregate[]> {
return this.http.get<Aggregate[]>('https://vz.home.peper.info/rest-vz/latest-data?houseId=' + houseId + '&timeWindow=' + duration);
}
getStatisticsWithDuration(duration: string | undefined): Observable<Statistics> {
getStatisticsWithDuration(duration: string | undefined, houseId: Number): Observable<Statistics> {
if (duration) {
return this.http.get<Statistics>('https://vz.home.peper.info/rest-vz/stats?houseId=1&duration=' + duration);
return this.http.get<Statistics>('https://vz.home.peper.info/rest-vz/stats?houseId=' + houseId + '&duration=' + duration);
} else {
return this.http.get<Statistics>('https://vz.home.peper.info/rest-vz/stats?houseId=1');
return this.http.get<Statistics>('https://vz.home.peper.info/rest-vz/stats?houseId=' + houseId);
}
}

View File

@ -1,8 +1,9 @@
import { Component, Input, OnInit } from '@angular/core';
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { Statistics } from '../statistics';
import { RestService } from '../rest.service';
import { formatDate, formatNumber } from '@angular/common';
import { NgIf } from '@angular/common';
import { numberAttribute } from '@angular/core';
import * as _ from 'lodash';
@Component({
@ -12,15 +13,22 @@ import * as _ from 'lodash';
templateUrl: './test.component.html',
styleUrl: './test.component.css'
})
export class TestComponent implements OnInit {
export class TestComponent implements OnInit, OnChanges {
statistics!: Statistics;
@Input() duration!: string;
@Input({ transform: numberAttribute }) houseId!: Number;
constructor(private rest: RestService) {
}
ngOnChanges(changes: SimpleChanges): void {
this.rest.getStatisticsWithDuration(this.duration, this.houseId).subscribe((data: Statistics) => {
this.statistics = data;
})
}
ngOnInit(): void {
this.rest.getStatisticsWithDuration(this.duration).subscribe((data: Statistics) => {
this.rest.getStatisticsWithDuration(this.duration, this.houseId).subscribe((data: Statistics) => {
this.statistics = data;
})
}