Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular CLI build using base-href and deploy-url to access assets on CDN

The background

I'm using Angular CLI to build a project (with multiple apps). I want to publish the apps on separate sub-paths on my domain, like example.com/apps/app1/.

If I set the --base-href parameter to /apps/app1/ it solves any issues regarding the router, and it will load the assets (JS, CSS, and images etc) just fine.

If I use the Location service, I can use

this.location.prepareExternalUrl('/assets/data/data.json') 

to resolve dynamically loaded assets (they will resolve to /apps/app1/assets/data/data.json).

So far so good. But I now want to serve the app assets through a CDN, such as cdn.example.com, while hosting the app itself on the original URL example.com/apps/app1/`. So now I build the app using:

 ng build -prod --app app1 --base-href "/apps/app1/" --deploy-url "http://cdn.example.com/app-assets/apps/app1/" 

This time, I apply both the --base-href and --deploy-url parameters. It works great in that it uses the base-href to help the Router resolve the URL and it loads the js and CSS files from the CDN. It also resolves the image URL references in the CSS files using the CDN URL.


The problem

When loading images or data from the assets folder dynamically (in a service or template), I can't find a good way for it to resolve the URLs using the deploy-url configuration.

If I use the Location service, it still uses the base-href to resolve URLs, so

this.location.prepareExternalUrl('/assets/data/data.json') 

will still resolve to /apps/app1/assets/data/data.json instead of http://cdn.example.com/app-assets/apps/app1/assets/data/data.json.

I would have expected it to use the deploy-url value if one is defined, especially since that would be a general solution that would work when hosting the files on the same domain and when hosting the files on an external domain.


The question

Is there a way to resolve the asset URLs considering both the base-href and the deploy-url parameters?

Ideally an official Angular function like Location.prepareExternalUrl, but if I can get the base-href and deploy-url parameters from Angular in some way, I could build my own service for it.

I would not want to define the URLs in the environment config since:

  1. It would require specific environment configs per app
  2. It creates a potential conflict with the values that are supplied when building the app.
like image 955
david.emilsson Avatar asked Dec 19 '17 11:12

david.emilsson


People also ask

What is BASE href in Angular build?

The base taglink During navigation, the Angular router uses the base href as the base path to component, template, and module files.

What is the use of base href in routing in index HTML page?

The href attribute specifies the base URL for all relative URLs on a page.

What is deploy URL in Angular?

--deploy-url A second parameter that is important is '--deploy-url'. This parameter will update the generated url's for our assets(scripts, css) inside the index. html. To make your assets available at /angularapp/, the deploy url should be set to /angularapp/.

What is the use of App_base_href?

APP_BASE_HREF returns a predefined DI token for the base href of the current page. The APP_BASE_HREF is the URL prefix that should be preserved. Approach: In app.


2 Answers

Here is how I solve this "BS"-problem. When your app is for e.g. hosted under www.domain.de/hehe.

  1. in angular.json add "deployUrl": "./",
  2. in src/index.html inside <head> add/update <base href="./">
  3. throughout your app you will use relative paths for assets and whatnot: <img src="./assets/img/pictureOfMyCrazyWife.png">
  4. just run ng build or ng build --prod conveniently.

with those simple steps you have a general setup. So it will work no matter if its www.domain.de/hehe, www.domain.de/hehe2, www.domain.de/ or www.domain.de/lol

like image 34
Andre Elrico Avatar answered Sep 21 '22 21:09

Andre Elrico


To access --deploy-url value at application runtime, create deploy-url.ts with:

export const DEPLOY_URL = new InjectionToken<string>('deployUrl'); 

And use this snippet in your main.ts file:

const deployUrl = (function() {   const scripts = document.getElementsByTagName('script');   const index = scripts.length - 1;   const mainScript = scripts[index];   return mainScript.src.replace(/main.*?\.js$/, ''); })();  const DEPLOY_URL_PROVIDER = {   provide: DEPLOY_URL,   useValue: deployUrl, };  platformBrowserDynamic([DEPLOY_URL_PROVIDER])   .bootstrapModule(AppModule)   .catch(err => console.error(err)); 

The idea is to get the url of currently executed Javascript file, which is main.js (or main.hash.js if outputHashing is enabled) and strip filename from it. Then in your services inject --deploy-url value with @Inject(DEPLOY_URL) deployUrl: string as a constructor parameter.

like image 51
Marcin Majkowski Avatar answered Sep 19 '22 21:09

Marcin Majkowski