Home > Net >  How can I set the Angular base path programmatically prior to initialization?
How can I set the Angular base path programmatically prior to initialization?

Time:02-23

Background / Goal

  • I have an app with multiple environments, each stored in a subfolder of a CDN.
    • e.g. cdn.com/dev, cdn.com/test
  • My goal is to load the dev site (cdn.com/dev/index.html) and have it load the corresponding resources from the /dev/ path.

⚠️ℹ️ We are doing a build-once, deploy-many strategy, so I do not have the ability to pass the baseHref as part of a build process or similar.

The Problem

So far no matter what I've tried, cdn.com/dev/index.html attempts to load scripts from the root (cdn.com/script.js) instead of the subfolder (cdn.com/dev/script.js)

What I've Tried

❌ Attempt 1: Set APP_BASE_HREF and use it as a provider

I set a window variable in index.html:

<script>
    window['base-href'] = window.location.pathname;
    console.log(window['base-href']);
</script>

Then I attempt to use that value to pass to APP_BASE_HREF as part of the provider, in app.module.ts:

import { APP_BASE_HREF } from '@angular/common';
let baseHref = (window as { [key: string]: any })["base-href"] as string;

if (!baseHref.endsWith("/")) {

  baseHref  = "/";

}

// I only updated the providers array below
@NgModule({
  declarations: [
    AppComponent,
    ComingSoonListComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule
  ],
  providers: [
    {
      provide: APP_BASE_HREF,
      useValue: baseHref
    }
  ],
  bootstrap: [AppComponent]
})

This did not appear to work.

❌ Attempt 2: A slightly less naive attempt

I remove the base tag from the index.html page on the off-chance it's overriding things.

In index.html, I call the script to capture the pathname:

<script>
    window['base-href'] = window.location.pathname;
    console.log(window['base-href']);
  </script>
</

In app.module.ts, I create a factory method that captures the window value, and then use it:

const baseHrefFactory = () => {
  let baseHref = (window as { [key: string]: any })["base-href"] as string;
  if (!baseHref.endsWith("/")) {

    baseHref  = "/";

  }
  console.log('TS: using baseHref of:', baseHref);
  return baseHref;
}

// Note I just use the factory method below

@NgModule({
  declarations: [
    AppComponent,
    ComingSoonListComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
  ],
  providers: [{ provide: APP_BASE_HREF, useFactory: baseHrefFactory }],
  bootstrap: [AppComponent],
})
export class AppModule { }
  • I can see the window value being set (per console logging)
  • It appears other files try to load before we can programmatically trigger the APP_BASE_HREF factory, and are attempted to load from the root path rather than the path I'd set. When running locally, it appears to work fine, but likely only because the path is / naturally.

❌ Attempt 3: Set the base href directly in HTML

In index.html in the <head> tag, after the base tag has been created:

  <script>
    var baseHref = window.location.pathname;
    if (!baseHref.endsWith("/")) {
      baseHref  = "/";
    }

    console.log("setting a baseHref of:", baseHref);
    document.getElementsByTagName("base")[0].href = baseHref;
  </script>
  • ✅ The index.html script I added is running (I see the console log)
  • ✅ The appropriate base path is being picked up (/dev/ – I see it in the console log)
  • ✅ The base tag is being set correctly (I can see this by inspecting via the browser dev tools)
  • ❌ The URL being requested in the browser does not contain /dev/
    • Actual URL: cdn.com/script.js
    • Expected URL: cdn.com/dev/script.js

❓(Not Yet attempted) Do I have to modify this at deploy time?

Do I need my deployment script to physically modify index.html to use the correct href in the <base> tag? That would be surprising, but I suppose it's possible. Seems like this would definitely be an answer; just hoping it's not the answer.

Question

How do I, at runtime, modify the base path so that Angular will download resources from the correct URL, including the sub-path from the CDN endpoint? Am I able to do this at runtime, or do I need to set this programmatically in the HTML at deployment time?

CodePudding user response:

Can you set the base href to something relative?

ng build --base-href ./

This will load the files relative to where your index file is.

CodePudding user response:

As far as I have been able to determine, the only ways to modify the base href is:

  • ❌ With the angular.json file -- but this would be applied at build time, and so wouldn't work for a build-once, deploy-many scenario
  • ❌ With the command line, e.g. ng build --base-href="./dev" -- but again, this is applied at build time, and wouldn't apply to all the deployed environments after that one build.
  • ❌ With the contents of the <base> tag within index.html itself. But again, when deployed to multiple places, this won't meet my needs.

So, as far as I am able to tell, the solution in this case for me will be to have the deployment script modify index.html to ensure the base tag within it aligns with the environment it's being deployed to.

  • Related