16

How to get APP_BASE_HREF programmatically?

I have this in my app.module.ts (where APP_BASE='/'):

{
    provide: APP_BASE_HREF,
    useValue: '<%= APP_BASE %>'
},

I'm trying:

import { APP_BASE_HREF } from '@angular/common'; 
...
console.debug(APP_BASE_HREF.toString());

and in console I get:

Token appBaseHref

and I need to get:

http://localhost:5555/
Cœur
  • 37,241
  • 25
  • 195
  • 267
surfealokesea
  • 4,971
  • 4
  • 28
  • 38

4 Answers4

34

Thanks to these answers by @Günter Zöchbauer:

...I have pieced together an approach like this:


index.html

<!DOCTYPE html>
<html lang="en-us">
<head>
    <base href="/" />
    <meta charset="utf-8" />
    <title>Testing APP_BASE_HREF</title>
    <link rel="icon" type="image/png" href="favicon.ico" sizes="16x16" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
        <app-root></app-root>
</body>
</html>

app.module.ts

import { APP_BASE_HREF, PlatformLocation } from "@angular/common";
import { AppComponent } from "./app.component";

/**
 * This function is used internal to get a string instance of the `<base href="" />` value from `index.html`.
 * This is an exported function, instead of a private function or inline lambda, to prevent this error:
 *
 * `Error encountered resolving symbol values statically.`
 * `Function calls are not supported.`
 * `Consider replacing the function or lambda with a reference to an exported function.`
 *
 * @param platformLocation an Angular service used to interact with a browser's URL
 * @return a string instance of the `<base href="" />` value from `index.html`
 */
export function getBaseHref(platformLocation: PlatformLocation): string {
    return platformLocation.getBaseHrefFromDOM();
}

@NgModule({
    declarations: [
        AppComponent
    ],
    providers: [
        {
            provide: APP_BASE_HREF,
            useFactory: getBaseHref,
            deps: [PlatformLocation]
        }
    ],
    bootstrap: [
        AppComponent
    ]
})
export class AppModule { }

app.component.ts

import { Component, OnInit, Inject } from "@angular/core";
import { APP_BASE_HREF } from "@angular/common";

@Component({
    selector: "app-root",
    templateUrl: "./app.component.html",
    styleUrls: ["./app.component.scss"]
})
export class AppComponent implements OnInit {

    constructor(@Inject(APP_BASE_HREF) private baseHref: string) {
        console.log(`APP_BASE_HREF is ${this.baseHref}`);
    }

    ngOnInit(): void {
    }
}

app.component.spec.ts

import { TestBed, ComponentFixture, async } from "@angular/core/testing";
import { APP_BASE_HREF } from "@angular/common";

describe("AppComponent", () => {

    let component: AppComponent,
        fixture: ComponentFixture<AppComponent>;

    // configuring dependencies
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [
                AppComponent
            ],
            providers: [
                {
                    provide: APP_BASE_HREF,
                    useValue: "/"
                }
            ]
        }).compileComponents();
    }));

    // getting component
    beforeEach(() => {
        fixture = TestBed.createComponent(AppComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    // tests
    it("should create the app", async(() => {
        const app: any = fixture.debugElement.componentInstance;
        expect(app).toBeTruthy();
    }));
});

Then you could do stuff like this:

  1. npm start

APP_BASE_HREF will equal the default value set in index.html

  1. npm run build --base-href /foobar/

APP_BASE_HREF will equal the value passed to the base-href flag

Ian Campbell
  • 2,678
  • 10
  • 56
  • 104
  • That's brilliant! – Wilgert Dec 03 '18 at 22:31
  • 2
    Thanks!! That solved my problem. It's a PITA having to inject the BaseRef and bind it to all src or href references, but at least it does work. Don't understand why this problem has not been addressed already. – VictorEspina Aug 10 '19 at 15:51
  • 3
    Note that if all you want to do is get the base href value then there are now easier approaches. See this [SO answer](https://stackoverflow.com/a/60705771/245602). – George Hawkins Jun 28 '20 at 12:57
22

Just inject it to a service or component like

import { APP_BASE_HREF } from '@angular/common'; 

...

constructor(@Inject(APP_BASE_HREF) private baseHref:string) {
  console.log(this.baseHref);
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Works, but only returns '/' – surfealokesea Sep 02 '16 at 08:20
  • Then the problem is where it is set. `useValue: '<%= APP_BASE %>'` might not do what you expect. Can you please post the full providers list (the ones that come before and after `provide: APP_BASE_HREF`)? – Günter Zöchbauer Sep 02 '16 at 08:23
  • 4
    It doesn't work for me. I have this error message: `No provider for Token appBaseHref` – cheb1k4 Mar 31 '17 at 14:11
  • @cheb Sounds like there is something wrong or missing in your code. Can you reproduce in a Plunker? https://plnkr.co/edit/J7YxmOTt3HDvNATGbxPm provides a ready-to-use Angular2 template. Just click "New" > "Angular" – Günter Zöchbauer Mar 31 '17 at 14:13
  • 4
    FYI: getting this error: `"No provider for InjectionToken appBaseHref"` – Serj Sagan Nov 09 '17 at 03:33
  • 2
    You need to provide it yourself `providers: [{provide: APP_BASE_HREF, useValue: '/my/app'}]`. The default is `/`. – Günter Zöchbauer Nov 09 '17 at 04:21
  • Angular 12, getting this error on the client: `NullInjectorError: R3InjectorError(AppModule)[InjectionToken appBaseHref -> InjectionToken appBaseHref -> InjectionToken appBaseHref]: NullInjectorError: No provider for InjectionToken appBaseHref!` – Mihai Marinescu Sep 28 '21 at 07:35
  • 1
    The answer is here: https://stackoverflow.com/a/66074792 You need to add `@Optional @Inject(APP_BASE_HREF)` You import Optional from angular/core – Mihai Marinescu Oct 07 '21 at 08:53
8

If using PathLocationStrategy, Angular now has LocationStrategy.getBaseHref() (https://angular.io/api/common/LocationStrategy).

For instance, in app.module.ts:

...
import { LocationStrategy, PathLocationStrategy } from '@angular/common';
...
@NgModule({
  providers: [
    ...
    {provide: LocationStrategy, useClass: PathLocationStrategy},
    ...
  ],
  ...
})
export class AppModule { }

And then in your services/components/...:

constructor(private locationStrategy: LocationStrategy, ...) {
  console.log(this.locationStrategy.getBaseHref());
  ...
}
matteo.cajani
  • 2,495
  • 4
  • 21
  • 19
  • 1
    I would like just to add that this works only if you use PathLocationStrategy. HashLocation strategy returns empty string for base href. – avidenic Mar 21 '19 at 08:01
0

Using Angular 14, this is working for me:

In app.module.ts import this:

import { APP_BASE_HREF } from '@angular/common';

Also in app.module.ts add this object inside @NgModule > providers array:

providers: [
    {
      provide: APP_BASE_HREF, useValue: '/my-directory/my-app'
    },

Optionally in angular.json (outputs same folder structure for build files) change:

"outputPath": "dist/my-app",

to this:

"outputPath": "dist/my-directory/my-app",

Finally use this build command in your terminal:

ng build --deploy-url /my-directory/my-app/
totallytotallyamazing
  • 2,765
  • 1
  • 20
  • 26