Change agm-map google maps API Key dynamically in Angular 7

I am building an app that lets you edit maps, the editor has google maps with the Agm map module, the end result for the user is an iframe with his map to embed into his webpage. I am using the module for the editor, and in the app.module.ts I import it with my API key.

import { AgmCoreModule } from '@agm/core';
...
imports: [
 ...
 AgmCoreModule.forRoot({
   apiKey: 'YOUR_KEY'
 })
]

The iframe will be a separate angular app that uses the same back-end. I would need the user's API key that will be fetched from the database but I can't get my head around on how can I build this part in Angular, it seems it creates the build with the api key, is it possible to do it in a way the key is modified on runtime? I've seen it done in jQuery. Where it can be fetched from the code in the script tag, or in vanilla js, an example:

<script async defer
    src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>

In this last example it can be easily done, Thank you

Upvotes: 2

Views: 5040

Answers (3)

Jaimin Patel
Jaimin Patel

Reputation: 11

AppModule.ts file

import {APP_INITIALIZER} from '@angular/core';
import { LAZY_MAPS_API_CONFIG, LazyMapsAPILoaderConfigLiteral } from '@agm/core';
import { GoogleMapsInitializer } from './shared/services/googleMapsInitializer';

imports: [
  AgmCoreModule.forRoot({
    apiKey: '',// this should be empty
    libraries: ['places']
  })
],
  providers: [
    {
      // APP_INITIALIZER is the Angular dependency injection token.
      provide: APP_INITIALIZER,
      // Pass in the AGM dependency injection token.
      deps: [LAZY_MAPS_API_CONFIG, GoogleMapsInitializer],
      // Allow for multiple startup injectors if needed.
      multi: true,
      // UseFactory provides Angular with the function to invoke.
      useFactory: (config: LazyMapsAPILoaderConfigLiteral, initializer: GoogleMapsInitializer) => () => initializer.initialize(config),
    }]

GoogleMapsInitializer service

import { LazyMapsAPILoaderConfigLiteral } from '@agm/core';
import { GoogleMapsConfigService } from '../rest-services/googleMapsConfigService';

export class GoogleMapsInitializer {
  constructor(private googleMapsConfigService: GoogleMapsConfigService) { }

  async initialize(config: LazyMapsAPILoaderConfigLiteral): Promise<void> {
    try {
      const apiKey = await this.googleMapsConfigService.getGoogleMapsApiKey();
      console.log("API KEY IN INITIALIZE SERVICE ==> ", apiKey)
      config.apiKey = apiKey;
    } catch (error) {
      console.error('Error setting Google Maps API key:', error);
    }
  }
}

GoogleMapsConfigService service

import { HttpClient } from '@angular/common/http';

export class GoogleMapsConfigService {
  constructor(private http: HttpClient) {}
  getGoogleMapsApiKey(): Promise<string> {
    const API_KEY_ENDPOINT = 'your api goes here';// get api key of google maps from backend service
    return this.http.post<any>(API_KEY_ENDPOINT, {}).toPromise()
      .then(response => response.apikey as string) // Replace 'apiKey' with the actual property name returned by your backend API.
      .catch(error => {
        console.error('Error fetching Google Maps API key:', error);
        return '';
      });
  }
}

During app startup, the APP_INITIALIZER in AppModule triggers the GoogleMapsInitializer service. The GoogleMapsInitializer service fetches the Google Maps API key using GoogleMapsConfigService. Once the key is obtained, it is dynamically set in the config object for AgmCoreModule.

Upvotes: 1

Arun Kumar
Arun Kumar

Reputation: 197

Adding to above answer, add the below to make the auto complete work

AgmCoreModule.forRoot({
  apiKey: 'initialKey',
  language: 'en',
  libraries: ['places']
}),

Upvotes: 1

I did solve it straight away, in my case I got the key from the API with this code

import { AgmCoreModule, LAZY_MAPS_API_CONFIG, LazyMapsAPILoaderConfigLiteral } 
from '@agm/core';

export function agmConfigFactory(http: HttpClient, config: LazyMapsAPILoaderConfigLiteral) {
    const id = window.location.pathname.replace(/\//g, "");
    return () => http.get<any>(`${environment.baseUrl}/map-display/${id}`).pipe(
        map(response => {
            config.apiKey = response.key;
            return response;
        })
    ).toPromise();
}

The content of the api call and the id can be substituted with the desired key.

import { AgmCoreModule, LAZY_MAPS_API_CONFIG, LazyMapsAPILoaderConfigLiteral } 
from '@agm/core';

@Injectable()
export class GoogleMapsConfig implements LazyMapsAPILoaderConfigLiteral {
  apiKey: string = CONFIG.googleMapsAPIKey;
}


you must add a provider specifying the function to the providers array in @NgModule, in my case:

  providers: [
    {
        provide: APP_INITIALIZER,
        useFactory: agmConfigFactory,
        deps: [HttpClient, LAZY_MAPS_API_CONFIG],
        multi: true
    } 
  ],

You can useClass instead of useFactory for the generic example I showed. You still have to provide an initial key in the imports array of @NgModule, it can be a meaningless string

    AgmCoreModule.forRoot({ apiKey: "initialKey"}),

Upvotes: 2

Related Questions