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
- e.g.
- 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_HREFfactory, 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
- Actual URL:
❓(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?