-
- @for (item of [
- { title: 'Explore the Docs', link: 'https://angular.dev' },
- { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' },
- { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' },
- { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' },
- { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' },
- ]; track item.title) {
-
- {{ item.title }}
-
-
+
+
+
+
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+ @if (loading) {
+
+
Loading energy price data...
+
+ }
+ @if (error) {
+
+ }
-
+ @if (!loading && !error) {
+
+ }
+
+
+ @if (!loading && !error && priceData.length > 0) {
+
+
Hour-by-hour prices
+
+
+
+ Time |
+ SEK/kWh |
+ EUR/kWh |
+
+
+
+ @for (price of priceData; track price.time_start) {
+
+ {{ price.time_start | date:'HH:00' }} - {{ price.time_end | date:'HH:00' }} |
+ {{ price.SEK_per_kWh | number:'1.2-4' }} |
+ {{ price.EUR_per_kWh | number:'1.2-4' }} |
+
+ }
+
+
+
+ }
diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts
deleted file mode 100644
index 5558c4f..0000000
--- a/src/app/app.component.spec.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { AppComponent } from './app.component';
-
-describe('AppComponent', () => {
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- imports: [AppComponent],
- }).compileComponents();
- });
-
- it('should create the app', () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app = fixture.componentInstance;
- expect(app).toBeTruthy();
- });
-
- it(`should have the 'Angular-DotIO' title`, () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app = fixture.componentInstance;
- expect(app.title).toEqual('Angular-DotIO');
- });
-
- it('should render title', () => {
- const fixture = TestBed.createComponent(AppComponent);
- fixture.detectChanges();
- const compiled = fixture.nativeElement as HTMLElement;
- expect(compiled.querySelector('h1')?.textContent).toContain('Hello, Angular-DotIO');
- });
-});
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index fbb0c2d..8e98ba0 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,12 +1,70 @@
-import { Component } from '@angular/core';
-import { RouterOutlet } from '@angular/router';
+import { Component, OnInit, inject } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { EnergyPriceService, EnergyPrice } from './energy-price.service';
+import { EnergyChartComponent } from './energy-chart/energy-chart.component';
@Component({
selector: 'app-root',
- imports: [RouterOutlet],
templateUrl: './app.component.html',
- styleUrl: './app.component.css'
+ styleUrls: ['./app.component.css'],
+ standalone: true,
+ imports: [CommonModule, FormsModule, EnergyChartComponent]
})
-export class AppComponent {
- title = 'Angular-DotIO';
+export class AppComponent implements OnInit {
+ title = 'Energy Price Dashboard';
+ priceData: EnergyPrice[] = [];
+ loading = true;
+ error = '';
+
+ // Default values
+ selectedDate = new Date();
+ selectedRegion = 'SE3'; // Stockholm / Södra Mellansverige as default
+
+ regions = [
+ { value: 'SE1', label: 'Luleå / Norra Sverige' },
+ { value: 'SE2', label: 'Sundsvall / Norra Mellansverige' },
+ { value: 'SE3', label: 'Stockholm / Södra Mellansverige' },
+ { value: 'SE4', label: 'Malmö / Södra Sverige' }
+ ];
+
+ private energyPriceService = inject(EnergyPriceService);
+
+ ngOnInit() {
+ this.loadPriceData();
+ }
+
+ loadPriceData() {
+ this.loading = true;
+ this.error = '';
+
+ const { year, month, day } = this.energyPriceService.formatDate(this.selectedDate);
+
+ this.energyPriceService.getPrices(year, month, day, this.selectedRegion)
+ .subscribe({
+ next: (data) => {
+ this.priceData = data;
+ this.loading = false;
+ },
+ error: (err) => {
+ this.error = 'Failed to load energy price data. Please try again later.';
+ this.loading = false;
+ console.error('Error fetching price data:', err);
+ }
+ });
+ }
+
+ onRegionChange() {
+ this.loadPriceData();
+ }
+
+ onDateChange(event: any) {
+ this.selectedDate = new Date(event.target.value);
+ this.loadPriceData();
+ }
+
+ get maxDate(): string {
+ const today = new Date();
+ return today.toISOString().split('T')[0];
+ }
}
diff --git a/src/app/app.config.ts b/src/app/app.config.ts
index a1e7d6f..6896639 100644
--- a/src/app/app.config.ts
+++ b/src/app/app.config.ts
@@ -2,7 +2,8 @@ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
+import { provideCharts, withDefaultRegisterables } from 'ng2-charts';
export const appConfig: ApplicationConfig = {
- providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)]
+ providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideCharts(withDefaultRegisterables())]
};
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
new file mode 100644
index 0000000..afe33a2
--- /dev/null
+++ b/src/app/app.module.ts
@@ -0,0 +1,25 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { HttpClientModule } from '@angular/common/http';
+import { FormsModule } from '@angular/forms';
+import { NgChartsModule } from 'ng2-charts';
+
+import { AppComponent } from './app.component';
+import { EnergyPriceService } from './energy-price.service';
+import { EnergyChartComponent } from './energy-chart/energy-chart.component';
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ EnergyChartComponent
+ ],
+ imports: [
+ BrowserModule,
+ HttpClientModule,
+ FormsModule,
+ NgChartsModule
+ ],
+ providers: [EnergyPriceService],
+ bootstrap: [AppComponent]
+})
+export class AppModule { }
diff --git a/src/app/energy-chart/energy-chart.component.css b/src/app/energy-chart/energy-chart.component.css
new file mode 100644
index 0000000..cf8b246
--- /dev/null
+++ b/src/app/energy-chart/energy-chart.component.css
@@ -0,0 +1,4 @@
+.chart-wrapper {
+ height: 400px;
+ width: 100%;
+}
diff --git a/src/app/energy-chart/energy-chart.component.html b/src/app/energy-chart/energy-chart.component.html
new file mode 100644
index 0000000..f0665d3
--- /dev/null
+++ b/src/app/energy-chart/energy-chart.component.html
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/src/app/energy-chart/energy-chart.component.ts b/src/app/energy-chart/energy-chart.component.ts
new file mode 100644
index 0000000..dc9849e
--- /dev/null
+++ b/src/app/energy-chart/energy-chart.component.ts
@@ -0,0 +1,105 @@
+import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ChartConfiguration, ChartData, ChartType } from 'chart.js';
+import { BaseChartDirective } from 'ng2-charts';
+import { EnergyPrice } from '../energy-price.service';
+
+@Component({
+ selector: 'app-energy-chart',
+ templateUrl: './energy-chart.component.html',
+ styleUrls: ['./energy-chart.component.css'],
+ standalone: true,
+ imports: [CommonModule, BaseChartDirective]
+})
+export class EnergyChartComponent implements OnChanges {
+ @Input() priceData: EnergyPrice[] = [];
+
+ // Chart configuration
+ public lineChartType: ChartType = 'line';
+
+ public lineChartData: ChartData<'line'> = {
+ datasets: [],
+ labels: []
+ };
+
+ public lineChartOptions: ChartConfiguration['options'] = {
+ responsive: true,
+ maintainAspectRatio: false,
+ scales: {
+ x: {
+ title: {
+ display: true,
+ text: 'Time'
+ }
+ },
+ y: {
+ title: {
+ display: true,
+ text: 'Price (SEK per kWh)'
+ },
+ beginAtZero: true
+ }
+ },
+ plugins: {
+ legend: {
+ display: true,
+ },
+ tooltip: {
+ callbacks: {
+ label: function(context) {
+ let label = context.dataset.label || '';
+ if (label) {
+ label += ': ';
+ }
+ if (context.parsed.y !== null) {
+ label += context.parsed.y.toFixed(2) + ' SEK/kWh';
+ }
+ return label;
+ }
+ }
+ }
+ }
+ };
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes['priceData'] && this.priceData) {
+ this.updateChartData();
+ }
+ }
+
+ private updateChartData(): void {
+ // Extract time labels and price values
+ const labels = this.priceData.map(item => {
+ const startTime = new Date(item.time_start);
+ return startTime.getHours() + ':00';
+ });
+
+ const sekPrices = this.priceData.map(item => item.SEK_per_kWh);
+ const eurPrices = this.priceData.map(item => item.EUR_per_kWh);
+
+ // Update chart data
+ this.lineChartData = {
+ labels: labels,
+ datasets: [
+ {
+ data: sekPrices,
+ label: 'SEK per kWh',
+ backgroundColor: 'rgba(66, 133, 244, 0.2)',
+ borderColor: 'rgb(66, 133, 244)',
+ pointBackgroundColor: 'rgb(66, 133, 244)',
+ fill: 'origin',
+ tension: 0.4
+ },
+ {
+ data: eurPrices,
+ label: 'EUR per kWh',
+ backgroundColor: 'rgba(15, 157, 88, 0.2)',
+ borderColor: 'rgb(15, 157, 88)',
+ pointBackgroundColor: 'rgb(15, 157, 88)',
+ fill: 'origin',
+ tension: 0.4
+ }
+ ]
+ };
+ }
+}
diff --git a/src/app/energy-price.service.ts b/src/app/energy-price.service.ts
new file mode 100644
index 0000000..33440e5
--- /dev/null
+++ b/src/app/energy-price.service.ts
@@ -0,0 +1,32 @@
+import { Injectable, inject } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs';
+
+export interface EnergyPrice {
+ SEK_per_kWh: number;
+ EUR_per_kWh: number;
+ EXR: number;
+ time_start: string;
+ time_end: string;
+}
+
+@Injectable({
+ providedIn: 'root'
+})
+export class EnergyPriceService {
+ private apiBaseUrl = 'https://www.elprisetjustnu.se/api/v1/prices';
+ private http = inject(HttpClient);
+
+ getPrices(year: string, month: string, day: string, priceClass: string): Observable
{
+ const url = `${this.apiBaseUrl}/${year}/${month}-${day}_${priceClass}.json`;
+ return this.http.get(url);
+ }
+
+ formatDate(date: Date): { year: string, month: string, day: string } {
+ const year = date.getFullYear().toString();
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
+ const day = date.getDate().toString().padStart(2, '0');
+
+ return { year, month, day };
+ }
+}
diff --git a/src/main.ts b/src/main.ts
index 35b00f3..4e6be2b 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,6 +1,11 @@
import { bootstrapApplication } from '@angular/platform-browser';
-import { appConfig } from './app/app.config';
+import { provideHttpClient } from '@angular/common/http';
+import { provideCharts, withDefaultRegisterables } from 'ng2-charts';
import { AppComponent } from './app/app.component';
-bootstrapApplication(AppComponent, appConfig)
- .catch((err) => console.error(err));
+bootstrapApplication(AppComponent, {
+ providers: [
+ provideHttpClient(),
+ provideCharts(withDefaultRegisterables())
+ ]
+}).catch(err => console.error(err));