DEV Community

Shawn Wildermuth
Shawn Wildermuth

Posted on • Originally published at wildermuth.com on

Using Angular's Base HREF in Paths

I was recently working with a client and they were having an odd problem with Angular. They’d build their Angular apps in isolation then move them into an ASP.NET Core project and their asset links would break. Let’s look at why this happens and how to address it.

The problem comes down to this simple idea. So the HTML looked like this:

<div>
  <img src="/assets/img/comingsoon.jpg" 
       alt="logo" 
       class="logo">
</div>

Enter fullscreen mode Exit fullscreen mode

This works when running the project directly via ‘ng serve’ because the file is in the assets folder:

Project

Project Path to /assets/img/comingsoon.jpg

When deploying the app to an ASP.NET Core project, it works…as soon as it’s being deployed to the root of project. The problem is that the Angular app just renders HTML for the browser to load the images. So the absolute URL (e.g. starting with a slash) assumes that it is going to be deployed to the root of the resulting webserver. But what if it’s not deployed that way. A common case is when it’s deployed to IIS as a website in a folder of a site. For example:

A nested app in IIS

In ASP.NET’s Razor handles this by using the tilde syntax:

<img src="~/img/2019/keynote-speaker.jpg" 
     alt-="Speaker Name" 
     class="img-responsive img-circle col-md-3" />

Enter fullscreen mode Exit fullscreen mode

The tilde (~) tells ASP.NET Core to use the root of the app (whether that be the root of the site, or the App root in IIS). For Angular, we needed a different approach. Luckily, Angular already needed to know this for their own needs.

In Angular, they use a header value (base) to specify where this app is hosted:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>NgRootdir</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root></app-root>
</body>
</html>

Enter fullscreen mode Exit fullscreen mode

The base element’s href tells Angular how to deal with URLs but it doesn’t extend to HTML markup. In order to use this, we have to do some work. I found this out through a StackOverflow question answered by Ian Campbell:

The trick is to create a function for returning the Base HREF from PlatformLocation:

export function getBaseHref(platformLocation: PlatformLocation): string {
  return platformLocation.getBaseHrefFromDOM();
}

Enter fullscreen mode Exit fullscreen mode

You can then use this to allow the value to be injected into components by creating a Provider:

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

Enter fullscreen mode Exit fullscreen mode

Once this is done, you can just inject the APP_BASE_HREF into your components via the @Inject decorator:

export class AppComponent {
  title = 'ng-rootdir';

  constructor(@Inject(APP_BASE_HREF) public baseHref:string) {
  }
}

Enter fullscreen mode Exit fullscreen mode

With that baseHref in your class, you can use it in the markup:

<div>
  <img [src]="baseHref + '/assets/img/comingsoon.jpg'" 
       alt="logo" 
       class="logo">
</div>

Enter fullscreen mode Exit fullscreen mode

By adding the baseHref to the start of the image URL, it can work in both scenarios. With this in place you can use the Base HREF in other places as well.

Interested?

Creative Commons License

This work by Shawn Wildermuth is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Based on a work at wildermuth.com.


If you liked this article, see Shawn's courses on Pluralsight.

Top comments (0)