From 0fcd22d989a57929f7638c9b88a687de6e0b02c9 Mon Sep 17 00:00:00 2001 From: Max <133934232+Kleostro@users.noreply.github.com> Date: Sat, 17 Aug 2024 18:26:12 +0300 Subject: [PATCH 1/5] feat: implement create station form --- .../create-station-form.component.html | 68 +++++++++++ .../create-station-form.component.scss | 27 +++++ .../create-station-form.component.ts | 108 ++++++++++++++++++ src/app/shared/constants/message-status.ts | 9 ++ 4 files changed, 212 insertions(+) create mode 100644 src/app/admin/components/create-station-form/create-station-form.component.html create mode 100644 src/app/admin/components/create-station-form/create-station-form.component.scss create mode 100644 src/app/admin/components/create-station-form/create-station-form.component.ts create mode 100644 src/app/shared/constants/message-status.ts diff --git a/src/app/admin/components/create-station-form/create-station-form.component.html b/src/app/admin/components/create-station-form/create-station-form.component.html new file mode 100644 index 0000000..0251781 --- /dev/null +++ b/src/app/admin/components/create-station-form/create-station-form.component.html @@ -0,0 +1,68 @@ +
+

Create Station

+
+ + + + + + + +
+ + diff --git a/src/app/admin/components/create-station-form/create-station-form.component.scss b/src/app/admin/components/create-station-form/create-station-form.component.scss new file mode 100644 index 0000000..d15a6de --- /dev/null +++ b/src/app/admin/components/create-station-form/create-station-form.component.scss @@ -0,0 +1,27 @@ +.form { + display: flex; + flex-direction: column; + gap: 1rem; + margin-top: 4.813rem; +} + +.controls { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.control { + width: 100%; + + &-label { + display: flex; + flex-direction: column; + gap: 0.5rem; + } +} + +.submit { + gap: 0.5rem; + justify-content: center; +} diff --git a/src/app/admin/components/create-station-form/create-station-form.component.ts b/src/app/admin/components/create-station-form/create-station-form.component.ts new file mode 100644 index 0000000..a6d5069 --- /dev/null +++ b/src/app/admin/components/create-station-form/create-station-form.component.ts @@ -0,0 +1,108 @@ +import { NgIf } from '@angular/common'; +import { ChangeDetectionStrategy, Component, inject, OnDestroy, OnInit, signal } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; + +import { MessageService } from 'primeng/api'; +import { ButtonModule } from 'primeng/button'; +import { FloatLabelModule } from 'primeng/floatlabel'; +import { InputNumberModule } from 'primeng/inputnumber'; +import { RippleModule } from 'primeng/ripple'; +import { ToastModule } from 'primeng/toast'; +import { map, Observable, of, Subscription, switchMap } from 'rxjs'; + +import { StationsService } from '@/app/api/stationsService/stations.service'; +import MESSAGE_STATUS from '@/app/shared/constants/message-status'; + +import { MapService } from '../../services/map/map.service'; + +@Component({ + selector: 'app-create-station-form', + standalone: true, + imports: [FloatLabelModule, InputNumberModule, ReactiveFormsModule, RippleModule, ButtonModule, NgIf, ToastModule], + providers: [MessageService], + templateUrl: './create-station-form.component.html', + styleUrl: './create-station-form.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class CreateStationFormComponent implements OnInit, OnDestroy { + private fb = inject(FormBuilder); + private mapService = inject(MapService); + private stationsService = inject(StationsService); + private messageService = inject(MessageService); + private subscription = new Subscription(); + + public isStationCreated = signal(false); + + private createStation(city: string, latitude: number, longitude: number): Observable<{ id: number }> { + return this.stationsService.createNewStation({ + city, + latitude, + longitude, + relations: [], + }); + } + + private createMarker(city: string, latitude: number, longitude: number): void { + this.mapService.createNewMarker({ + city, + lat: latitude, + lng: longitude, + }); + } + + public createStationForm = this.fb.nonNullable.group({ + city: ['', [Validators.required.bind(this)]], + latitude: [0, [Validators.required.bind(this)]], + longitude: [0, [Validators.required.bind(this)]], + }); + + public submitForm(): void { + this.createStationForm.markAllAsTouched(); + this.createStationForm.updateValueAndValidity(); + + if (this.createStationForm.valid) { + this.isStationCreated.set(true); + const { city, latitude, longitude } = this.createStationForm.getRawValue(); + this.subscription.add( + this.stationsService + .isStationInCity(city) + .pipe( + switchMap((exists) => + exists + ? of(null) + : this.createStation(city, latitude, longitude).pipe( + map((id) => { + this.isStationCreated.set(false); + this.createStationForm.reset(); + this.messageService.add({ + severity: MESSAGE_STATUS.SUCCESS, + summary: 'Success!', + detail: `Station created with id: ${id.id}`, + }); + }), + ), + ), + map((exists) => (exists ? of(null) : this.createMarker(city, latitude, longitude))), + ) + .subscribe({ + error: (error: Error) => { + this.isStationCreated.set(false); + this.messageService.add({ severity: MESSAGE_STATUS.ERROR, summary: error.name, detail: error.message }); + }, + }), + ); + } + } + + public ngOnInit(): void { + this.subscription.add( + this.mapService.getLngLat().subscribe((lngLat) => { + this.createStationForm.patchValue({ longitude: lngLat.lng, latitude: lngLat.lat }); + }), + ); + } + + public ngOnDestroy(): void { + this.subscription.unsubscribe(); + } +} diff --git a/src/app/shared/constants/message-status.ts b/src/app/shared/constants/message-status.ts new file mode 100644 index 0000000..29ffc06 --- /dev/null +++ b/src/app/shared/constants/message-status.ts @@ -0,0 +1,9 @@ +const MESSAGE_STATUS = { + SUCCESS: 'success', + ERROR: 'error', + INFO: 'info', + WARNING: 'warning', + DEFAULT: 'default', +} as const; + +export default MESSAGE_STATUS; From f35d9520c458cc95456131c59f30658da53680d5 Mon Sep 17 00:00:00 2001 From: Max <133934232+Kleostro@users.noreply.github.com> Date: Sat, 17 Aug 2024 18:27:12 +0300 Subject: [PATCH 2/5] feat: implement map --- .../admin/components/map/map.component.html | 5 ++ .../admin/components/map/map.component.scss | 16 ++++ src/app/admin/components/map/map.component.ts | 83 +++++++++++++++++++ src/app/admin/services/.gitkeep | 0 src/app/admin/services/map/map.service.ts | 43 ++++++++++ 5 files changed, 147 insertions(+) create mode 100644 src/app/admin/components/map/map.component.html create mode 100644 src/app/admin/components/map/map.component.scss create mode 100644 src/app/admin/components/map/map.component.ts delete mode 100644 src/app/admin/services/.gitkeep create mode 100644 src/app/admin/services/map/map.service.ts diff --git a/src/app/admin/components/map/map.component.html b/src/app/admin/components/map/map.component.html new file mode 100644 index 0000000..7af8fac --- /dev/null +++ b/src/app/admin/components/map/map.component.html @@ -0,0 +1,5 @@ +
+
+ +
+
diff --git a/src/app/admin/components/map/map.component.scss b/src/app/admin/components/map/map.component.scss new file mode 100644 index 0000000..a8820d6 --- /dev/null +++ b/src/app/admin/components/map/map.component.scss @@ -0,0 +1,16 @@ +:host { + display: flex; +} + +.map-wrap { + position: relative; + width: 100%; + height: calc(100vh - 4.813rem); + margin-top: 4.813rem; +} + +.map { + position: absolute; + width: 100%; + height: 100%; +} diff --git a/src/app/admin/components/map/map.component.ts b/src/app/admin/components/map/map.component.ts new file mode 100644 index 0000000..167f069 --- /dev/null +++ b/src/app/admin/components/map/map.component.ts @@ -0,0 +1,83 @@ +import { + AfterViewInit, + ChangeDetectionStrategy, + Component, + ElementRef, + inject, + OnDestroy, + OnInit, + signal, + ViewChild, +} from '@angular/core'; + +import { Map, NavigationControl } from 'maplibre-gl'; +import { SkeletonModule } from 'primeng/skeleton'; +import { Subscription } from 'rxjs'; + +import ENVIRONMENTS from '@/environment/environment'; + +import { INITIAL_MAP_STATE } from '../../constants/initial-map-state'; +import { MapService } from '../../services/map/map.service'; + +@Component({ + selector: 'app-map', + standalone: true, + imports: [SkeletonModule], + templateUrl: './map.component.html', + styleUrl: './map.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MapComponent implements OnInit, AfterViewInit, OnDestroy { + private mapService = inject(MapService); + private subscription = new Subscription(); + private map!: Map; + + @ViewChild('map') + private mapContainer!: ElementRef; + + public isMapLoaded = signal(false); + + private initMap(): void { + this.map = new Map({ + container: this.mapContainer.nativeElement, + style: `https://api.maptiler.com/maps/streets-v2/style.json?key=${ENVIRONMENTS.MAP_KEY}`, + center: [INITIAL_MAP_STATE.lng, INITIAL_MAP_STATE.lat], + zoom: INITIAL_MAP_STATE.zoom, + }); + + this.map.addControl(new NavigationControl({})); + } + + private initMapClickHandler(): void { + this.map.on('click', ({ lngLat }) => { + this.mapService.setLngLat({ lng: lngLat.lng, lat: lngLat.lat }); + }); + + this.map.on('load', () => { + this.isMapLoaded.set(true); + }); + } + + public ngOnInit(): void { + this.subscription.add( + this.mapService.getNewMarker().subscribe((marker) => { + marker.addTo(this.map); + marker.togglePopup(); + this.map.flyTo({ + center: [marker.getLngLat().lng, marker.getLngLat().lat], + zoom: INITIAL_MAP_STATE.zoom, + }); + }), + ); + } + + public ngAfterViewInit(): void { + this.initMap(); + this.initMapClickHandler(); + } + + public ngOnDestroy(): void { + this.map.remove(); + this.subscription.unsubscribe(); + } +} diff --git a/src/app/admin/services/.gitkeep b/src/app/admin/services/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/admin/services/map/map.service.ts b/src/app/admin/services/map/map.service.ts new file mode 100644 index 0000000..ea014e2 --- /dev/null +++ b/src/app/admin/services/map/map.service.ts @@ -0,0 +1,43 @@ +import { Injectable } from '@angular/core'; + +import { Marker, Popup } from 'maplibre-gl'; +import { BehaviorSubject, Subject } from 'rxjs'; + +import makeFirstLetterToUppercase from '@/app/shared/utils/makeFirstLetterToUppercase'; + +import { MARKER_PARAMS } from '../../constants/initial-map-state'; +import createNewPopupOffsets from '../../utils/createNewPopupOffsets'; + +@Injectable({ + providedIn: 'root', +}) +export class MapService { + private lngLat = new BehaviorSubject({ lng: 0, lat: 0 }); + private newMarker = new Subject(); + + public createNewMarker({ city, lng, lat }: { city: string; lng: number; lat: number }): void { + this.newMarker.next( + new Marker({ color: MARKER_PARAMS.color }) + .setLngLat([lng, lat]) + .setPopup( + new Popup({ className: 'map-popup' }) + .setLngLat({ lng, lat }) + .setText(makeFirstLetterToUppercase(city)) + .setOffset(createNewPopupOffsets()) + .setMaxWidth(MARKER_PARAMS.max_width), + ), + ); + } + + public getLngLat(): BehaviorSubject<{ lng: number; lat: number }> { + return this.lngLat; + } + + public setLngLat({ lng, lat }: { lng: number; lat: number }): void { + this.lngLat.next({ lng, lat }); + } + + public getNewMarker(): Subject { + return this.newMarker; + } +} From 92bd4225700f3b8894e689ba0596fab9e66913fa Mon Sep 17 00:00:00 2001 From: Max <133934232+Kleostro@users.noreply.github.com> Date: Sat, 17 Aug 2024 18:28:01 +0300 Subject: [PATCH 3/5] feat: api key for map --- src/environment/environment.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/environment/environment.ts diff --git a/src/environment/environment.ts b/src/environment/environment.ts new file mode 100644 index 0000000..14f31f1 --- /dev/null +++ b/src/environment/environment.ts @@ -0,0 +1,5 @@ +const ENVIRONMENTS = { + MAP_KEY: '1bdk2M9O2x8FBX6D1NjO', +} as const; + +export default ENVIRONMENTS; From 2899081faa79884986f3d7c17b014699b442484a Mon Sep 17 00:00:00 2001 From: Max <133934232+Kleostro@users.noreply.github.com> Date: Sat, 17 Aug 2024 18:28:43 +0300 Subject: [PATCH 4/5] feat: inital map state --- angular.json | 1 + package.json | 3 ++ public/styles/common.scss | 8 +++++ .../components/sidebar/sidebar.component.scss | 35 +++++++++---------- src/app/admin/constants/initial-map-state.ts | 8 +++++ .../admin-layout/admin-layout.component.scss | 4 +++ .../pages/stations/stations.component.html | 5 ++- .../pages/stations/stations.component.scss | 7 ++++ .../pages/stations/stations.component.spec.ts | 22 ------------ .../pages/stations/stations.component.ts | 5 ++- src/app/admin/utils/createNewPopupOffsets.ts | 23 ++++++++++++ .../api/stationsService/stations.service.ts | 10 +++++- .../utils/makeFirstLetterToUppercase.ts | 3 ++ tsconfig.json | 2 ++ 14 files changed, 93 insertions(+), 43 deletions(-) create mode 100644 src/app/admin/constants/initial-map-state.ts delete mode 100644 src/app/admin/pages/stations/stations.component.spec.ts create mode 100644 src/app/admin/utils/createNewPopupOffsets.ts create mode 100644 src/app/shared/utils/makeFirstLetterToUppercase.ts diff --git a/angular.json b/angular.json index 93dbf3b..dc611de 100644 --- a/angular.json +++ b/angular.json @@ -33,6 +33,7 @@ "styles": [ "node_modules/primeng/resources/themes/lara-light-blue/theme.css", "node_modules/primeng/resources/primeng.min.css", + "node_modules/maplibre-gl/dist/maplibre-gl.css", "src/styles.scss" ], "stylePreprocessorOptions": { diff --git a/package.json b/package.json index b7ad44b..b1e8a88 100644 --- a/package.json +++ b/package.json @@ -35,11 +35,14 @@ "@ngrx/store": "^18.0.2", "@ngrx/store-devtools": "^18.0.1", "@planess/train-a-backend": "^0.0.3", + "@types/geojson": "^7946.0.14", "@types/jest": "^29.5.12", + "@types/maplibre-gl": "^1.14.0", "eslint-plugin-unused-imports": "^4.1.3", "express": "^4.18.2", "jest": "^29.7.0", "jest-preset-angular": "^14.2.2", + "maplibre-gl": "^4.5.2", "modern-normalize": "^3.0.0", "primeicons": "^7.0.0", "primeng": "^17.18.9", diff --git a/public/styles/common.scss b/public/styles/common.scss index cea2d00..6565ad2 100644 --- a/public/styles/common.scss +++ b/public/styles/common.scss @@ -23,6 +23,14 @@ body { body { margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', + 'Droid Sans', 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } .hidden { diff --git a/src/app/admin/components/sidebar/sidebar.component.scss b/src/app/admin/components/sidebar/sidebar.component.scss index 71acb3d..9c2f644 100644 --- a/src/app/admin/components/sidebar/sidebar.component.scss +++ b/src/app/admin/components/sidebar/sidebar.component.scss @@ -5,12 +5,28 @@ height: 100%; background-color: $gray-200; border-right: $one solid $gray-100; + + ::ng-deep .p-element { + display: flex; + align-items: center; + justify-content: center; + + width: $offset-ms; + height: $offset-ms; + padding: 0; + + font-size: $font-size-xs; + + &.link-back { + margin: $offset-xxs; + margin-right: 0; + } + } } .links { display: flex; flex-direction: column; - height: 100%; } .link { @@ -38,20 +54,3 @@ background-color: $gray-100; } } - -::ng-deep .p-element { - display: flex; - align-items: center; - justify-content: center; - - width: $offset-ms; - height: $offset-ms; - padding: 0; - - font-size: $font-size-xs; - - &.link-back { - margin: $offset-xxs; - margin-right: 0; - } -} diff --git a/src/app/admin/constants/initial-map-state.ts b/src/app/admin/constants/initial-map-state.ts new file mode 100644 index 0000000..52fe797 --- /dev/null +++ b/src/app/admin/constants/initial-map-state.ts @@ -0,0 +1,8 @@ +export const INITIAL_MAP_STATE = { lng: 2.349014, lat: 48.864716, zoom: 10 }; +export const MARKER_PARAMS = { + height: 40, + radius: 40, + offset: 20, + max_width: '5rem', + color: '#3B82F6', +}; diff --git a/src/app/admin/layout/admin-layout/admin-layout.component.scss b/src/app/admin/layout/admin-layout/admin-layout.component.scss index 82c0881..6d11024 100644 --- a/src/app/admin/layout/admin-layout/admin-layout.component.scss +++ b/src/app/admin/layout/admin-layout/admin-layout.component.scss @@ -5,3 +5,7 @@ grid-template-columns: calc($offset-xxxl * 2.6) 1fr; height: 100vh; } + +.content { + padding: 0 $offset-s; +} diff --git a/src/app/admin/pages/stations/stations.component.html b/src/app/admin/pages/stations/stations.component.html index 4fded1c..303cf8b 100644 --- a/src/app/admin/pages/stations/stations.component.html +++ b/src/app/admin/pages/stations/stations.component.html @@ -1 +1,4 @@ -

stations works!

+
+ + +
diff --git a/src/app/admin/pages/stations/stations.component.scss b/src/app/admin/pages/stations/stations.component.scss index e69de29..19857da 100644 --- a/src/app/admin/pages/stations/stations.component.scss +++ b/src/app/admin/pages/stations/stations.component.scss @@ -0,0 +1,7 @@ +@import 'variables'; + +.wrapper { + display: grid; + grid-template-columns: 1fr 25rem; + gap: $offset-s; +} diff --git a/src/app/admin/pages/stations/stations.component.spec.ts b/src/app/admin/pages/stations/stations.component.spec.ts deleted file mode 100644 index c4c2ab4..0000000 --- a/src/app/admin/pages/stations/stations.component.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { StationsComponent } from './stations.component'; - -describe('StationsComponent', () => { - let component: StationsComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [StationsComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(StationsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/admin/pages/stations/stations.component.ts b/src/app/admin/pages/stations/stations.component.ts index dbfbcbe..1fe9baa 100644 --- a/src/app/admin/pages/stations/stations.component.ts +++ b/src/app/admin/pages/stations/stations.component.ts @@ -1,9 +1,12 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { CreateStationFormComponent } from '../../components/create-station-form/create-station-form.component'; +import { MapComponent } from '../../components/map/map.component'; + @Component({ selector: 'app-stations', standalone: true, - imports: [], + imports: [MapComponent, CreateStationFormComponent], templateUrl: './stations.component.html', styleUrl: './stations.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/app/admin/utils/createNewPopupOffsets.ts b/src/app/admin/utils/createNewPopupOffsets.ts new file mode 100644 index 0000000..6788d66 --- /dev/null +++ b/src/app/admin/utils/createNewPopupOffsets.ts @@ -0,0 +1,23 @@ +import { Offset, Point } from 'maplibre-gl'; + +import { MARKER_PARAMS } from '../constants/initial-map-state'; + +const createNewPopupOffsets = (): Offset => ({ + top: new Point(0, 0), + left: new Point(MARKER_PARAMS.radius, (MARKER_PARAMS.height - MARKER_PARAMS.radius) * -1), + right: new Point(-MARKER_PARAMS.radius, (MARKER_PARAMS.height - MARKER_PARAMS.radius) * -1), + bottom: new Point(0, -MARKER_PARAMS.height), + center: new Point(0, MARKER_PARAMS.height), + 'top-left': new Point(0, 0), + 'top-right': new Point(0, 0), + 'bottom-left': new Point( + MARKER_PARAMS.offset, + (MARKER_PARAMS.height - MARKER_PARAMS.radius + MARKER_PARAMS.offset) * -1, + ), + 'bottom-right': new Point( + -MARKER_PARAMS.offset, + (MARKER_PARAMS.height - MARKER_PARAMS.radius + MARKER_PARAMS.offset) * -1, + ), +}); + +export default createNewPopupOffsets; diff --git a/src/app/api/stationsService/stations.service.ts b/src/app/api/stationsService/stations.service.ts index 6ed2c49..3f8ec43 100644 --- a/src/app/api/stationsService/stations.service.ts +++ b/src/app/api/stationsService/stations.service.ts @@ -1,7 +1,7 @@ import { HttpClient } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; +import { map, Observable } from 'rxjs'; import { NewStation, Station } from '../models/stations'; @@ -23,4 +23,12 @@ export class StationsService { public deleteStation(id: number): Observable { return this.httpClient.delete(`${this.STATION_ENDPOINT}/${id}`); } + + public isStationInCity(station: string): Observable { + return this.getStations().pipe( + map((stations) => + stations.some((stationFromList) => stationFromList.city.toLowerCase() === station.toLowerCase()), + ), + ); + } } diff --git a/src/app/shared/utils/makeFirstLetterToUppercase.ts b/src/app/shared/utils/makeFirstLetterToUppercase.ts new file mode 100644 index 0000000..55d500e --- /dev/null +++ b/src/app/shared/utils/makeFirstLetterToUppercase.ts @@ -0,0 +1,3 @@ +const makeFirstLetterToUppercase = (text: string): string => text.charAt(0).toUpperCase() + text.slice(1); + +export default makeFirstLetterToUppercase; diff --git a/tsconfig.json b/tsconfig.json index 41de90c..da35bd4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,8 @@ "outDir": "./dist/out-tsc", "strict": true, "noImplicitOverride": true, + "forceConsistentCasingInFileNames": true, + "downlevelIteration": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, From b4105443332ba085b2649d908027911d4128d942 Mon Sep 17 00:00:00 2001 From: Max <133934232+Kleostro@users.noreply.github.com> Date: Sat, 17 Aug 2024 18:40:12 +0300 Subject: [PATCH 5/5] refactor: replace ngIf with if --- .../create-station-form.component.html | 30 +++++++++++++------ .../create-station-form.component.ts | 3 +- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/app/admin/components/create-station-form/create-station-form.component.html b/src/app/admin/components/create-station-form/create-station-form.component.html index 0251781..2aeaccf 100644 --- a/src/app/admin/components/create-station-form/create-station-form.component.html +++ b/src/app/admin/components/create-station-form/create-station-form.component.html @@ -13,9 +13,13 @@

Create Station

autocomplete="off" /> @if (createStationForm.controls.city; as city) { - - Please enter a city! - + @if (city.invalid && city.dirty) { + + @if (city.hasError('required')) { + Please enter a city! + } + + } } @@ -30,9 +34,13 @@

Create Station

class="p-inputtext control" /> @if (createStationForm.controls.latitude; as latitude) { - - Please enter a latitude! - + @if (latitude.invalid && latitude.dirty) { + + @if (latitude.hasError('required')) { + Please enter a latitude! + } + + } } @@ -47,9 +55,13 @@

Create Station

class="p-inputtext control" /> @if (createStationForm.controls.longitude; as longitude) { - - Please enter a longitude! - + @if (longitude.invalid && longitude.dirty) { + + @if (longitude.hasError('required')) { + Please enter a longitude! + } + + } } diff --git a/src/app/admin/components/create-station-form/create-station-form.component.ts b/src/app/admin/components/create-station-form/create-station-form.component.ts index a6d5069..b75a4ad 100644 --- a/src/app/admin/components/create-station-form/create-station-form.component.ts +++ b/src/app/admin/components/create-station-form/create-station-form.component.ts @@ -1,4 +1,3 @@ -import { NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, inject, OnDestroy, OnInit, signal } from '@angular/core'; import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; @@ -18,7 +17,7 @@ import { MapService } from '../../services/map/map.service'; @Component({ selector: 'app-create-station-form', standalone: true, - imports: [FloatLabelModule, InputNumberModule, ReactiveFormsModule, RippleModule, ButtonModule, NgIf, ToastModule], + imports: [FloatLabelModule, InputNumberModule, ReactiveFormsModule, RippleModule, ButtonModule, ToastModule], providers: [MessageService], templateUrl: './create-station-form.component.html', styleUrl: './create-station-form.component.scss',