Table of Contents | ||
---|---|---|
|
Example 1: MAP + Angular library
Description
In this example we will create a new tab in the object area, which will show a map with our current location.
Implementation
Use a CLI command to generate a plug-in component with name map.
Code Block language bash linenumbers true eo g plugin map
Use a CLI command to install the maps package (takes some time).
Code Block language bash linenumbers true npm install -P @agm/core
Import AgmCoreModule module to CustomPluginsModule.
Code Block language js title custom-plugins.module.ts linenumbers true import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {EoFrameworkModule} from '@eo-sdk/client'; import {PluginsModule} from '@eo-sdk/client'; import {EoPlugin} from '@eo-sdk/client'; import {links} from '../custom-states/custom-states.module'; import {MapComponent} from './map/map.component'; import {AgmCoreModule} from '@agm/core'; export const entryComponents: EoPlugin[] = [ MapComponent, ]; @NgModule({ imports: [ CommonModule, EoFrameworkModule, AgmCoreModule.forRoot(), PluginsModule.forRoot(entryComponents, links) ], declarations: [MapComponent], exports: [PluginsModule] }) export class CustomPluginsModule { }
Update map.component.ts to load the current position from navigator, and modify the matchType property if necessary.
Code Block language js title map.component.ts linenumbers true import { Component, OnInit } from '@angular/core'; @Component({ selector: 'eo-map', templateUrl: './map.component.html', styleUrls: ['./map.component.scss'] }) export class MapComponent implements OnInit { static id = 'eo.custom.plugin.map'; static matchType = new RegExp ('object-details-tab.*'); currentPosition; secureOriginIssue = false; constructor() { } private getCurrentPosition(): void { navigator.geolocation.getCurrentPosition((position) => { this.currentPosition = position; }, failure => { if (failure.message.indexOf('Only secure origins are allowed') === 0) { this.secureOriginIssue = true; } }); } ngOnInit() { this.getCurrentPosition(); } }
Update map.component.html to include a map component with the latitude and longitude based on the current position.
Code Block language xml title map.component.html linenumbers true <agm-map *ngIf="currentPosition && !secureOriginIssue" [latitude]="currentPosition.coords.latitude" [longitude]="currentPosition.coords.longitude" [style.height.%]="90"> <agm-marker [latitude]="currentPosition.coords.latitude" [longitude]="currentPosition.coords.longitude"></agm-marker> </agm-map> <section *ngIf="!currentPosition && secureOriginIssue" class="secure-origin__issue"> <eo-icon class="no-file" [iconSrc]="'assets/_default/svg/ic_not_listed_location.svg'"></eo-icon> <span translate>eo.custom.plugin.map.secure.origin</span> </section>
To make it look nice, we add some scss
Code Block language css title map.component.scss linenumbers true .secure-origin__issue { height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; text-transform: capitalize; border: 0; .no-file { width: 128px; height: 128px; opacity: .06; } }
Use a CLI command to generate labels/translations
Code Block language bash linenumbers true eo g label eo.custom.plugin.map --en Map --de Stadtplan
Code Block language bash linenumbers true eo g label eo.custom.plugin.map.secure.origin --en "Please, change to a secure connection (https)" --de "Bitte wechseln sie zu einer sicheren verbindung (https)"
...
- We start by extending the "MAP via iframe + core service"
For readability, we move most of our functionality to a service
Code Block language bash title Terminal ng g s eo-custom-client/services/location --spec false
To keep the code organized, we introduce LocationType enum.
Code Block language bash title Terminal ng g enum eo-custom-client/enum/LocationType
Update locationType.ts
Code Block language js title locationTypes.rs linenumbers true export enum LocationType { COORDS = 'coords', ADDRESS = 'address', EMPTY = 'empty' }
To keep the code organized, we introduce an Interfaces for the Locations.
Code Block language bash title Terminal ng g interface eo-custom-client/interfaces/Location
Update location.interface.ts
Code Block language js title location.interface.ts linenumbers true export interface LocationByAddress { type: string; streethw: string; townhw: string; countryhw: string } export interface LocationByCoords { type: string; photogpsla: number; photogpslo: number; } export interface NoLocation { type: string; }
Final location.service.ts. Make sure the normalize function matches your scheme properties!
Code Block language js title location.service.ts linenumbers true import {Injectable} from '@angular/core'; import {LocationType} from '../enum/location-type.enum'; import {Observable, of, throwError} from 'rxjs'; import {LocationByAddress, LocationByCoords, NoLocation} from '../interfaces/location'; @Injectable({ providedIn: 'root' }) export class LocationService { private readonly apiKey = `AIzaSyDX8znfh-d4u3spGhC1GvCjq6EA1pjPovQ`; private readonly mapUrl = `https://www.google.com/maps/embed/v1/place`; /** * normalize Address data - map your data based on scheme properties * @param data * @returns */ private normalize(data: any = {}): any { return { streethw: data.strassehw, townhw: data.orthw, countryhw: data.landhw, photogpsla: data.imagegpsla, photogpslo: data.imagegpslo, ...data }; } /** * retrieve Address from data object * @param data * @returns */ private locationbDataWithAddress(data): LocationByAddress | NoLocation { const {streethw, townhw, countryhw} = data; return (townhw && countryhw) ? {type: LocationType.ADDRESS, streethw, townhw, countryhw} : {type: LocationType.EMPTY}; } /** * retrieve Geo Coordinates from data object * @param data * @returns */ private locationbDataWithCoords(data): LocationByCoords | NoLocation { const {photogpsla, photogpslo} = data; return (photogpsla && photogpslo) ? {type: LocationType.COORDS, photogpsla, photogpslo} : {type: LocationType.EMPTY}; } /** * Geo Coordinates are assumed to be only present in photo Objects. * We check the data to either retrieve Geo Coordinates or Address Data. * If neither is available we return empty type. * * @param typeName * @param data * @returns */ locationbData(typeName: string, data: Object): LocationByAddress | LocationByCoords | NoLocation { const normalizedData = this.normalize(data); return normalizedData.photogpsla ? this.locationbDataWithCoords(normalizedData) : this.locationbDataWithAddress(normalizedData); } /** * Depending on the Location type we build a different URL. * If we have no Location type we return an error. * * @param location * @returns */ mapsUrl(location): Observable<string> { let params: string; if (location.type === LocationType.ADDRESS) { const {streethw, townhw, countryhw} = location; params = `${streethw || ''}+${townhw}+${countryhw}`; } else if (location.type === LocationType.COORDS) { const {photogpsla, photogpslo} = location; params = `${photogpsla},${photogpslo}`; } else { return throwError(true) } return of(`${this.mapUrl}?key=${this.apiKey}&q=${params}`); } }
Since we want to hide the map when we don't have any location information, we need to update the map.component.html.
Code Block language xml title map-frame.component.html linenumbers true <iframe class="map" [hidden]="mapAvailable" #mapFrame frameborder="0" allowfullscreen> </iframe> <section class="map__not-available" [hidden]="!mapAvailable"> <eo-icon class="no-file" [iconSrc]="'assets/_default/svg/ic_no-file.svg'"></eo-icon> </section>
As a last step in the TS files we need to update map-frame.component.ts
Code Block language js title map-frame.component.ts linenumbers true import { Component, AfterViewInit, ViewChild, ElementRef, Renderer2 } from '@angular/core'; import {DmsService, DmsObject, EventService, EnaioEvent, Event} from '@eo-sdk/core'; import {SelectionService, UnsubscribeOnDestroy} from '@eo-sdk/client'; import {takeUntil} from 'rxjs/operators'; import {LocationService} from '../../services/location.service'; @Component({ selector: 'eo-map-frame', templateUrl: './map-frame.component.html', styleUrls: ['./map-frame.component.scss'] }) export class MapFrameComponent extends UnsubscribeOnDestroy implements AfterViewInit { static id = 'eo.custom.plugin.map-frame'; static matchType = new RegExp('object-details-tab.*'); context; mapAvailable; @ViewChild('mapFrame') mapFrame: ElementRef; constructor(private selectionService: SelectionService, private dmsService: DmsService, private renderer: Renderer2, private eventService: EventService, private locationService: LocationService) { super(); } /** * Process dmsObject to get the url for map frame * @param dmsObj */ setupMap(dmsObj: DmsObject) { if (dmsObj) { const {typeName, data} = dmsObj; const location = this.locationService.locationbData(typeName, data); this.locationService .mapsUrl(location) .pipe( takeUntil(this.componentDestroyed$) ) .subscribe(url => { this.mapAvailable = false; this.renderer.setAttribute(this.mapFrame.nativeElement, 'src', url); }, error => this.mapAvailable = error); } } /** * Load & update current context/dmsObject * @param event */ eventHandler(event: Event) { if (event.type === EnaioEvent.DMS_OBJECT_LOADED || (this.context && this.context.id === event.data.id)) { this.context = event.data; this.setupMap(event.data); } } ngAfterViewInit() { this.eventService .on(EnaioEvent.DMS_OBJECT_LOADED, EnaioEvent.DMS_OBJECT_UPDATED) .pipe( takeUntil(this.componentDestroyed$) ) .subscribe(e => this.eventHandler(e)); } }
To finish this plug-in, add some styling. Plus In addition, we are able to display the map - frame plugin only per specific types that contains that contain Geo Coordinates or Address Data.
Code Block language css title map-frame.component.scss linenumbers true .map{ width: 100%; height: 90%; &__not-available{ height: 100%; display: flex; justify-content: center; align-items: center; text-transform: capitalize; border: 0; .no-file{ width: 128px; height: 128px; opacity: .06; } } } // Display map- frame plugin only per specific types that containscontain Geo Coordinates or Address Data ::ng-deep { // by default hide map-frame plugin (tab content & tab label) eo-object-details [id="eo.custom.plugin.map-frame"], eo-object-details [id="eo.custom.plugin.map-frame-label"] { display: none; } // display map- frame only per specific types eo-object-details[data-type=personalakte] [id="eo.custom.plugin.map-frame"], eo-object-details[data-type=personalakte] [id="eo.custom.plugin.map-frame-label"], eo-object-details[data-type=photobasic] [id="eo.custom.plugin.map-frame"], eo-object-details[data-type=photobasic] [id="eo.custom.plugin.map-frame-label"], eo-object-details[data-type=foto] [id="eo.custom.plugin.map-frame"], eo-object-details[data-type=foto] [id="eo.custom.plugin.map-frame-label"], eo-object-details[data-type=albumphoto] [id="eo.custom.plugin.map-frame"], eo-object-details[data-type=albumphoto] [id="eo.custom.plugin.map-frame-label"] { display: block; } }
...