2

I would like to put environment variable from main.ts in index.html <script src="..."> tag. Index.html look like this:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>RoadFaultReporter</title>
        <base href="./" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" type="image/x-icon" href="favicon.ico" />
        <link rel="manifest" href="manifest.webmanifest" />
        <meta name="theme-color" content="#1976d2" />
        <link rel="preconnect" href="https://fonts.gstatic.com" />
        <link
            href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"
            rel="stylesheet"
        />
    </head>
    <body>
        <app-root></app-root>
        <noscript>Please enable JavaScript to continue using this application.</noscript>
        <script src="https://maps.googleapis.com/maps/api/js?key=MY_KEY"></script>
    </body>
</html>

In the place of MY_KEY in <script src=""> I would like to place a key from variable - environment.googleMapsApi. Is there any way to do this?

2 Answers 2

3

No there is not, but you can do it the Angular way; By adding a provided to AppModule you force the whole app initialization to wait until google maps are loaded.

The code below is the one I created. I created it before the official package was created but I still use mine as it is without dependency and 450 lines shorter.

app.module.ts

@NgModule({
    //...
    providers: [
        {
            provide: APP_INITIALIZER,
            useValue: () => loadGoogleMaps(environment.googleMapsKey),
            multi: true,
        },
    ],
})
export class AppModule {}

loadGoogleMaps

const CALLBACK_NAME = 'initMap';

export enum GoogleMapsLibraries {
    /** provides a graphical interface for users to draw polygons, rectangles, polylines, circles, and markers on the map. Consult the [Drawing library documentation](https://developers.google.com/maps/documentation/javascript/drawinglayer) for more information. */
    drawing = 'drawing',
    /** includes utility functions for calculating scalar geometric values (such as distance and area) on the surface of the earth. Consult the [Geometry library documentation](https://developers.google.com/maps/documentation/javascript/geometry) for more information. */
    geometry = 'geometry',
    /** shows users key places of interest near a location that you specify. Consult the Local [Context library documentation](https://developers.google.com/maps/documentation/javascript/local-context) for more information. */
    localContext = 'localContext',
    /** enables your application to search for places such as establishments, geographic locations, or prominent points of interest, within a defined area. Consult the [Places library documentation](https://developers.google.com/maps/documentation/javascript/places) for more information. */
    places = 'places',
    /** provides heatmaps for visual representation of data. Consult the [Visualization library documentation](https://developers.google.com/maps/documentation/javascript/visualization) for more information. */
    visualization = 'visualization',
};

export function loadGoogleMaps(googleMapsKey: string, libraries: GoogleMapsLibraries[] = []) {
    if (!window) {
        return Promise.resolve();
    }

    return new Promise<void>((resolve, reject) => {
        function onError(err?: any) {
            // eslint-disable-next-line @typescript-eslint/no-empty-function
            (window as any)[CALLBACK_NAME] = () => {}; // Set the on load callback to a no-op
            scriptElement.removeEventListener('error', onError);
            reject(err || new Error('Could not load the Google Maps API'));
        }

        // Reject the promise after a timeout
        const timeoutId = setTimeout(() => onError(), 10000);

        // Hook up the on load callback
        (window as any)[CALLBACK_NAME] = () => {
            clearTimeout(timeoutId);
            scriptElement.removeEventListener('error', onError);
            resolve();
            delete (window as any)[CALLBACK_NAME];
        };

        // Deduplicate libraries
        libraries = [...new Set(libraries)];

        // Prepare the `script` tag to be inserted into the page
        const scriptElement = document.createElement('script');
        scriptElement.addEventListener('error', onError);
        scriptElement.async = true;
        scriptElement.defer = true;
        scriptElement.src = `https://maps.googleapis.com/maps/api/js?key=${googleMapsKey}&region=Cz&language=cs&callback=${CALLBACK_NAME}&libraries=${libraries.join(',')}`;
        document.head.appendChild(scriptElement);
    });
}
Sign up to request clarification or add additional context in comments.

2 Comments

APP_INITIALIZER works with useValue, not useFactory?
@robert this works in my case, thought after looking into docs, the correct way may be to pass it to a factory...
0

If you are using a recent Angular version (such as 19), you can use @ngx-env/builder and create an .env file (or environment variables) and inject them into your html this way:

key: "%NG_APP_GOOGLE_KEY%"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.