Reputation: 2331
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.
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.
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:
Upvotes: 43
Views: 23300
Reputation: 641
You can use NgOptimizedImage directive to load images from CDN URL. The most basic set-up using environments
files with different URL for different environments is below:
// app.module.ts
import { NgOptimizedImage } from '@angular/common';
...
providers: [{
provide: IMAGE_LOADER,
useValue: (config: ImageLoaderConfig) => {
return `${environment.imageURL}${config.src}`;
},
}],
...
// component.html
<img ngSrc="assets/images/image.svg" width="100" height="100"/>
Upvotes: 0
Reputation: 11490
Here is how I solve this "BS"-problem. When your app is for e.g. hosted under
www.domain.de/hehe
.
angular.json
add "deployUrl": "./",
src/index.html
inside <head>
add/update <base href="./">
<img src="./assets/img/pictureOfMyCrazyWife.png">
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
Upvotes: 4
Reputation: 1821
I ran into this exact same situation, and as you outlined in The Background, building with both the base-href
and deploy-url
set does work in that we're able to serve up our css and js files from a CDN while hosting the application on another server.
As a solution to dynamically loaded assets in our templates, we wrote an API that delivers environment variables for this purpose which delivers the appropriate URLs needed per deployment.
Upvotes: 0
Reputation: 420
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.
Upvotes: 8
Reputation: 717
Here is how I have done for few projects.
Set the APP_BASE_HREF using a dynamic value set by server E.g. Cookie and SPA reads it on init. getBaseUrl function simply read a cookie value.
{ provide: APP_BASE_HREF, useFactory: () => getBaseUrl() }
Set the deployUrl as part of your CI process. You need to set the BUILD and VERSION dynamically. This can be done by using a simple shell script.
https://my-cloud.cloudfront.net/my-app/{BUILD}/{VERSION}/
Upvotes: -2