Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to initilize the module with variable apiKey #882

Closed
gmaggiodev opened this issue Feb 3, 2017 · 12 comments
Closed

how to initilize the module with variable apiKey #882

gmaggiodev opened this issue Feb 3, 2017 · 12 comments

Comments

@gmaggiodev
Copy link

I have the google api key in a singleton configuration service (a way to share the app configuration accross all the application).

How can I pass the apiKey variable the module inside @NgModule?

`@NgModule({

imports: [
BrowserModule,
CommonModule,
FormsModule,
AgmCoreModule.forRoot({
apiKey: 'YOUR_KEY' // <-- I cannot use a constant!!! How can I pass a variable?
})
],
providers: [],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})`

@gmaggiodev gmaggiodev changed the title how to initilize the module with varaible apiKey how to initilize the module with variable apiKey Feb 3, 2017
@jor-rit
Copy link

jor-rit commented Feb 3, 2017

Depends on what kind of build tool you are using (e.g. angular-cli or custom webpack or ...) and where the variable should come from.
This can be a little tricky. Specially when you also want to use AOT compiling, which prevents too much dynamic code inside the @NgModule.

The solution I currently implemented is overriding the 'lazy config provider' in my main app.module.

In the module where I actually use the map, I use an empty config in the forRoot:

AgmCoreModule.forRoot()

In my app.module.ts:
import the injection token:

import { LAZY_MAPS_API_CONFIG }
  from 'angular2-google-maps/core/services';

Add to providers in @NgModule:

providers: [
    {provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig}
  ],

And implement that 'GoogleMapsConfig' class, which should implement the LazyMapsAPILoaderConfigLiteral interface (from ' from 'angular2-google-maps/core/services').

@Injectable()
class GoogleMapsConfig {
  apiKey: string;
...
  constructor() {
    apiKey = getMyApiKeyFromSomewhere()
...
  }
}

In that injectable I can inject other services and read the config from somewhere.
(e.g. if you use angular-cli you can import environments there). Or maybe read out the domain from the browser... or call an serverside API...

@kyranjamie
Copy link

kyranjamie commented Feb 27, 2017

I just tried the above. It manages to circumvent AoT errors, but the API key isn't being passed to Google Maps – so it returns key missing error.

In my use case, I'm using the extended-define-webpack-plugin to add a compile-time global config settings (API keys that can change based on build type etc).

import { LAZY_MAPS_API_CONFIG, LazyMapsAPILoaderConfigLiteral } from 'angular2-google-maps/core/services';

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

@NgModule({
  declarations: [...],
  imports: [...],
  providers: [{provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig}],
  bootstrap: [AppComponent]
})
export class AppModule {}

And, in my lazy loaded module where I'm using the map, calling AgmCoreModule.forRoot()

@daBishMan
Copy link

I am using angular 4 and CLI 1.0.2, I am trying to get the above code by @kyranjamie but I cannot get this to work, what we are trying is basically the same set up a different key for prod vs. dev

@daBishMan
Copy link

this is my code, what am I doing wrong?

`
@Injectable()
export class GoogleMapsConfig implements LazyMapsAPILoaderConfigLiteral {
public apiKey: string;
constructor() {
if (environment.production) {
this.apiKey = KEY INSERTED HERE
};
}
}
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule,
HttpModule,
AppRoutingModule,
NgbModule.forRoot(),
NouisliderModule,
AgmCoreModule.forRoot()
],
providers: [
{ provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig }
],
bootstrap: [AppComponent]
})
export class AppModule {

}
`

@SaxyPandaBear
Copy link

Are you sure that if (environment.production) will ever resolve to true?

@daBishMan
Copy link

I have resolved this thanks for your feedback

@bradrust
Copy link

@daBishMan or @kyranjamie , If you guys ever figured this out, could you post your final solution? @kyranjamie , my issue appears to be similar to yours,... on a product build where AOT is concerned, agm-map initialization in the request to maps.google.com,... no URL API-KEY parameter is being sent. Therefore, it comes back with "missing api key" errors.

@bradrust
Copy link

@jorrit-wehelp, thank you for your guidance. I am including below what is working for me....

import {Injectable} from "@angular/core";
import {LazyMapsAPILoaderConfigLiteral} from "@agm/core";
import {Config} from "../providers/config";

@Injectable()
export class MapsConfig implements LazyMapsAPILoaderConfigLiteral{
  public apiKey: string
  public libraries: string[]
  constructor(config: Config) {
    this.apiKey = config.get("MAP_API_JS_KEY")
    this.libraries = ['places']
    console.log("lazy map init with " + this.apiKey)
  }
}

in my main @NgModule, I have this in providers...

    {
      provide: LAZY_MAPS_API_CONFIG,
      useClass: MapsConfig,
      deps: [Config]
    }

The reference to the Config class is a build-time webpack variable-replace which setups of a map of variables used in Dev or Prod modes.

@shehnaztj
Copy link

shehnaztj commented Sep 1, 2017

Hello

I was able to achieve the dynamic key, as by @jorrit-wehelp comments, but in my scenario after the user logout and loggedIn the agmKey value is not updating and not able to see the map

what I did was like in our module component we added the following code
So I moved the class GoogleMapsConfig and {provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig} inside map @component

import { LAZY_MAPS_API_CONFIG , LazyMapsAPILoaderConfigLiteral} from '@agm/core';
import {AuthService} from '@pl-core/_services';

@Injectable();
class GoogleMapsConfig implements LazyMapsAPILoaderConfigLiteral {
  public apiKey: string ;
  constructor() {
    console.log('INSIDE MAPs'); //This is not displayed in console
     this.apiKey = _authService.currentUser() && _authService.currentUser().agmKey ? _authService.currentUser().agmKey : '';
    _authService.getMapKey$.subscribe((key) => {
      this.apiKey = key;
    })
  }
}

@Component({
  selector: 'map-comp',
  templateUrl: './dyn-map.component.html',
  styleUrls: ['./dyn-map.component.scss'],
  providers: [{provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig}],
  encapsulation : ViewEncapsulation.None
})

after we logout and login with an other user the control is not coming inside the GoogleMapsConfig class what can I do to solve this issue??

@inorganik
Copy link

I had @jorrit-wehelp's solution working but only intermittently because I had a race condition where my service that returns the api key would not always return it in time for the element to try and render. In order to force to wait until the api key was returned I simply did this:

constructor of my map component:

constructor(private envService: EnvironmentService) {
    this.envService.environment$.subscribe(environment => {
      // setTimeout is to wait a beat since my GoogleMapsConfig receives
      // the key at the same instant.
      setTimeout(() => this.gmapsApiKeyReady = true);
    });
  }

Then in the markup:

<agm-map *ngIf="gmapsApiKeyReady" ...></agm-map>

@willwolfram18
Copy link

I don't know if this will apply to everyone or if this is more of a case by case basis, but I discovered this is the result of Angular's AOT compilation process, as opposed to an issue with agm itself. Here is an overview of the situation I had.

I wanted to make the API key config driven instead of a hard coded string, such that moving through different environments would allow me to use different API keys and monitor the API's usage. In order to do this, I had my .NET back-end inject some JavaScript with the value I needed into the global namespace, stored in Example.Namespace.GoogleMapsApiKey. With that in mind, here was my TypeScript code.

// typings.d.ts
declare namespace Example.Namespace {
    export const GoogleMapsApiKey: string;
}

// app.module.ts
import { AgmCoreModule } from "@agm/core";
import { NgModule } from "@angular/core";

@NgModule({
    // ...
    imports: [
        AgmCoreModule.forRoot({
            apiKey: Example.Namespace.GoogleMapsApiKey,
        })
    ],
    // ...
})
export class AppModule {}

I thought creating a type definition for some const global variable with the value I needed would get included during the AOT build process. However, as it turns out the AOT compiler only supports a subset of TypeScript tools, which prevented the variable from getting included at compilation time. Effectively what was happening was an "Only initialized variables and constants" error without an error getting emitted from the Angular CLI. Once I made the switch to use some kind of LAZY_MAPS_API_CONFIG provider using a class I was able to successfully .

@stale
Copy link

stale bot commented Dec 12, 2018

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Dec 12, 2018
@stale stale bot closed this as completed Dec 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants