Closed feng-xiao closed 2 years ago
That's also an issue for us. We have a widgets application that contains multiples lazy loaded custom elements. These elements are used in other applications. When some element want to load an image it should be loaded from the widgets domain, not from the current one that belongs to main application. Currently this can be achieved by using deploy-url during widgets app build. How to do it without this option?
@feng-xiao, can you please elaborate on why you need the deployUrl option and the specific use-case?
@artaommahe, is the image located in component stylesheet or external stylesheet?
@alan-agius4 in component's scss files
@artaommahe, in that case there is no alternate option for that. One thing to point out is that this aligns the behaviour of images defined in HTML and CSS, which results in images URI HTML to always be relative to HTML.
What comes to mind is to have an external CSS file (styles.css) that you defined the image in, since images are loaded relative to the stylesheet origin. Though this might be a bit cumbersome.
@alan-agius4 using global css file breaks lazy loading of such stuff, locality of code (images of some component from one of the widgets lib somehow are placed in global styles file).
This looks strange due to availability of official @angular/elements
and breaks their usage for micro-frontend cases.
If the assets can be referenced from the component templates instead, another option is to generate the full asset URL at runtime via the use of import.meta.url
. This is similar in functionality to the deprecated deploy URL option but has the advantage of not needing to hardcode anything other than the relative path of the asset. The JavaScript and asset files as a whole could then potentially be served from locations other than a predefined deploy URL and still function.
This method is unfortunately not possible with stylesheets since they are fixed at build time. However, if stylesheets are required in this context, they could potentially be external but dynamically loaded to retain lazy loading semantics.
@feng-xiao, can you please elaborate on why you need the deployUrl option and the specific use-case?
@artaommahe, is the image located in component stylesheet or external stylesheet?
Hi @alan-agius4 , I've integrated Angular app into an ASP.NET MVC application, i.e. the Angular app is initialized by a MVC razor view. Since the Angular resources are generated and hosted in different port number in 'ng serve' mode than MVC app, e.g. ASP.NET MVC app is loaded in localhost:8444, Angular app would be loaded in different port number like localhost:8445, I need to make Angular resources loaded using absolute URL, otherwise, they'll be loaded in wrong URL, the one MVC is using (i.e. localhost:8444).
@feng-xiao, can you please elaborate on why you need the deployUrl option and the specific use-case? @artaommahe, is the image located in component stylesheet or external stylesheet?
Hi @alan-agius4 , I've integrated Angular app into an ASP.NET MVC application, i.e. the Angular app is initialized by a MVC razor view. Since the Angular resources are generated and hosted in different port number in 'ng serve' mode than MVC app, e.g. ASP.NET MVC app is loaded in localhost:8444, Angular app would be loaded in different port number like localhost:8445, I need to make Angular resources loaded using absolute URL, otherwise, they'll be loaded in wrong URL, the one MVC is using (i.e. localhost:8444).
Hi @alan-agius4 , based on webpack's source code, I don't see any overhead by setting 'publicPath' to webpack config. I don't know why Angular Doc says it could increase build time.
@feng-xiao, the deployUrl
, is used not just to set the publicPath
, using deployUrl
will cause additional steps including processing all CSS templates and files to append the deployUrl
to the assets urls.
Another disadvantage of using deployUrl
, is that the string value will be included all over the bundle multiple times, which causes an increase in bundle size and make promotion of bundles from one environment to another rather tricky.
In your cases, the recommended approach would be to use a combination of both --base-href
and APP_BASE_HREF
.
Razor View
...
<base href="http://localhost:8445/">
...
app.module.ts
...
{ provide: APP_BASE_HREF, useValue: 'http://localhost:8444' }
...
@alan-agius4 We heavily use too the deployUrl parameter. For us, it's because we have a reverse proxy that route all requests under (base-href)/static
(if the load is too high) to a dedicated apache server, because we serve angular through a tomcat server most of the time (embedded jar for a Spring Boot application).
With the deployUrl we currently have
<!DOCTYPE html><html lang="fr">
<head>
<base href="/context/path/">
<meta charset="utf-8">
<title>My application...</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="static/favicon.png">
<link rel="stylesheet" href="static/styles.8277a10e7c2be6c58f14.css" media="print" onload="this.media='all'">
</head>
<body>
<app-root>loading...</app-root>
<script src="static/runtime-es2017.5a110097badef840dfda.js" type="module" crossorigin="use-credentials"></script>
<script src="static/runtime-es5.5a110097badef840dfda.js" nomodule defer></script>
<script src="static/polyfills-es5.1f50460b6153455d79c6.js" nomodule defer></script>
<script src="static/polyfills-es2017.acf5917270495d93417a.js" type="module" crossorigin="use-credentials"></script>
<script src="static/main-es2017.f3526c48e4006bc6f2e3.js" type="module" crossorigin="use-credentials"></script>
<script src="static/main-es5.f3526c48e4006bc6f2e3.js" nomodule defer></script>
</body>
</html>
If this parameter is removed, we're just screwed :/
Also, the context path
, in tomcat, must match base-href
for angular to work correctly. And last, but not least, requests served to /context/path/
are protected by an SSO platform (OpenAM), whereas, those under /context/path/static
are not, because considered safe (not enforced policy) by convention. So the SSO agent is free from checking if requests behind static
are legitimate or not, what drastically reduces load. Hope it's understandable.
TLDR : we'd love to keep the option to have both base-href and deployUrl (or whatever it's named) configurable.
A full example project is here : https://github.com/mpalourdio/SpringBootAngularHTML5
Our dev build relies on deployUrl
to properly set resource URLs for ng serve
, since we're not using a proxy (and don't want to), we simply link files from https://localhost:4200
from our .cshtml
file. Removing the option from ng serve
settings made me create an additional builder definition, where I still able to set this to https://localhost:4200
and everything works. Removing the option from builder settings will ruin our set up.
Same for me , in asp.net loaded from index.cshtml like mpalourdio... would break everything... ( application is deployed on premise so the url is not known before deployment ) Now it tries to load scripts and assets from the root web folder while it should load from a sub folder (so security can be set on the application to allow only downloading scripts and assets from subfolder).
I have mentioned my use-case in https://github.com/angular/angular-cli/issues/22208
Currently I am using the following option for building the application,
"scripts": {
...
"build": "node --max_old_space_size=5048 ./node_modules/@angular/cli/bin/ng build --base-href=/portal/ --deploy-url=/portal/public/ --configuration production"
...
}
In my case, static assets needs to be load from /portal/public/
In our case, we are doing white-listing the security for the path having public in it as it has only static assets.
What would be your recommendation in this case?
I tried with steps mentioned in https://github.com/angular/angular-cli/issues/22208#issuecomment-975312544 but the issue didnt got resolved. Please see https://github.com/angular/angular-cli/issues/22208#issuecomment-975409891
deprecation of deployUrl
is now been handled in my environment with https://github.com/angular/angular-cli/issues/22208#issuecomment-975570531.
Note: In my case, i am using relative path
Can someone explain how I can avoid having all the garbage ("main.js", "runtime.js") etc in the root folder with base href="/" when deployUrl is removed? I don't want to write a script that rewrites urls. I just want to run "ng build --watch" and get the correct result from an already running webserver when I change files.
I get this with --base-url="/" and --deploy-url="/app".
(I don't have performance issues, so… why create problems for no good reason?)
@alan-agius4 similar case as above - we're adopting module federation for some of our projects to split huge apps into smaller pieces to reduce development and deployment time cost. We have to use deploy-url
to pin remote apps' css images to their domain, that is different to host's one.
Curious, what's the Angular Team's argument for removing deployUrl
from ng serve
targets? We are heavily relying on this feature with our custom dev-server
builder and is preventing us from migrating to Angular 13.
@alan-agius4 Is there any hope of Angular reverting the change to deployUrl
for serve
targets, or should we invest in trying to find another solution?
@Blackbaud-SteveBrush, in your case you can set the deployUrl
in the browser builder which will also effect ng serve
.
However, this issue is related to removed deployUrl
completely.
What is your use-case of using this option?
We have a custom dev-server
builder that calls into the "vanilla" Angular CLI dev-server
builder. Before we make the call to the dev-server
builder, we create a deployUrl
value that points to a remote URL that also includes metadata in a parameter that's different depending on the SPA running the builder. Since the value is dynamic, we can't bake it into the angular.json
file. For example, our deployUrl
looks like:
https://host.blackbaud.com/my-spa?config=<names-of-chunks,assets,localhost-url,etc.>
The URL above (i.e. deployUrl
) is where we house the SPA's index.html
file, but we read the assets/chunks from localhost (during development). Using the deployUrl
allows us to use this functionality (during a serve) because that value is getting passed directly to webpack's publicPath
.
If there's an alternative way to generate a dynamic publicPath
during a serve, I'd be happy to hear it. 👍 Thanks!
We have a similar case where we define deployUrl
(in angular.json) our CDN address, which is then added to all the images and scripts (in index.html and CSS files).
This allows us to load our assets from our CDN, while sending the HTTP requests to our server (the domain that we ran at, while baseHref is '/').
Setting the baseHref
to be the CDN domain (without deployUrl
) causes our app to send all of the HTTP requests to the CDN instead of to our domain - which breaks the app.
@alan-agius4 - Once deployUrl
is deprecated, how can we keep having the CDN defined (like publicPath
in WebPack) without breaking the app's HTTP requests?
We have a similar problem without deployUrl. We have had ability to serve 2 different versions, Dev served from local files and Prod served from CDN. The HTML would load files based on deployUrl + fileName
while keeping the base context the same. Using baseHref
would change the entire page context. In short, they are not equivalent to our use cases.
As a work around for the WebPack publicPath
value you can set the global __webpack_public_path__
variable in your angular entry point. I have the following in my main.ts
file.
declare var __webpack_public_path__: string;
__webpack_public_path__ = 'valueFormerlyAssignedUsing_deployUrl';
If you have different build configurations you can store the appropriate value in the './environments/environment
file and have those be replaced according to the angular.json
configuration.
I do want to upvote this question though because it is a breaking change without any angular specific work around. We also use asp.net mvc to host the root file (instead of the angular index.html file) so the angular distribution folder is no the same as the load point url that asp.net uses. This means that anything dynamically loaded with import
will fail because it can't be resolved.
We also have a setup that is heavily dependent on deployUrl
. In our case, our developers run ng serve
on localhost
, but load the application on a remote development server that also contains API endpoints, as well as additional js resources unrelated to Angular. By setting deployUrl
to //localhost:4200/
, and leaving the baseUrl as /
, we can get the behavior we need, in a way that most closely resembles our production code.
Then, for production, we deploy all of our angular code to a subdirectory dedicated to the code generated by Angular. Setting deployUrl
to https://our.staticdomain.com/angular/
also perfectly suits our needs.
I'm not sure how we would handle this if we couldn't set deployUrl
.
@karptonite - does setting __webpack_public_path__
instead of deployUrl
solve your problem? See also this previous comment
@IgorWolbers it does seem to solve it, although I would, as you suggest, have to mess with environments to get it to work for both development and production.
But setting a webpack variable globally like that seems like a brittle solution--theoretically, an angular user could be agnostic with regard to the build system, aside from the options in angular.json.
@karptonite
it does seem to solve it, although I would, as you suggest, have to mess with environments to get it to work for both development and production.
I have it configured that way in our code and it runs well. It is the most flexible option AFAIK.
But setting a webpack variable globally like that seems like a brittle solution--theoretically, an angular user could be agnostic with regard to the build system, aside from the options in angular.json.
True but then you would likely not have this error to begin with as deployUrl
never integrated with other build systems in this manner other than WebPack AFAIK.
You are correct that the solution is not elegant, it is not (well) documented, and it would be nicer to call an angular specific API that would do this for you like deloyUrl
used to do. That said at least there is a work around that can be used in the interim. Let's hope that the angular team rethinks their plans for deployUrl
.
@alan-agius4 Our use case is also CDN. The application itself is hosted through ASP.NET, where style and scripts are from CDN. Same as few others, where base href won't help, as we don't want API traffic goes to CDN.
If we use GitHub as an example, their CSS style assets is under https://github.githubassets.com/assets/dark-xxxx.css
. Their script is also from similar location. Such as https://github.githubassets.com/assets/chunk-frameworks-xxxx.js
.
Where GitHub's route is under https://github.com/angular/angular-cli/issues/22113
.
It is common for many SPA to choose such approach. Where assets location is different to the application location. This is not unreasonable requirement, also with CI/CD, how many really care about how long it takes to build if it is required for an essential approach they want to achieve?
We have a somewhat special use case with the Magnolia CMS, which has an SPA Editor with support for Angular. We also use SSR, which means that Angular Universal and Magnolia are running side-by-side, with the Universal-Server being the entrypoint for both e.g. website and CMS (for the authors).
When using the author pages of the CMS, Magnolia will consume the index.html and change the base-href to the current page, before returning it to the user. This in turn means that our scripts (runtime.js, vendor.js etc.) in the body won't be resolved correctly when we cannot use absolute paths.
That is why we rely on being able to specify this absolute path with deployUrl
.
TL;DR
Our page can be accessed through different baseHrefs, that depend on the context from where they are accessed, and that means we need absolute paths in our resources to be able to load them from the same location all the time.
We too rely heavily on deployUrl with the same CDN use case as i see mentioned often here, Should deprecating deployUrl perhaps be reconsidered, it appears to be widely used and needed
I'll add my 2 cents: we don't use Angular-generated index.html
to serve the app, we use Rails instead. So, previously we used --deploy-url
to point to Webpack Dev Server for ng serve
and to point to CDN for production build (ng build
).
Now angular team suggests to use a combination of base href
and APP_BASE_HREF
instead of deploy-url
option, but it just doesn't work without introduction of many ugly hacks. Let's take a "production build" case as an example. Here is the list of things we have to do:
<base href>
to https://some.cdn.com/
for all the assets to be served correctly.APP_BASE_HREF
to location.origin
in order for the router to behave correctly<link href="https://my-app.com/manifest.json" rel="manifest">
, <meta content="https://my-app.com/browserconfig.xml" name="msapplication-config">
etc. as without it they'll be loaded from CDN.(hack) Introduce a IgnoreBaseHrefInterceptor
which will prepend location.origin
to all requests because without it all of them will go to https://some.cdn.com/
(browser uses <base href>
value also for all XHR requests).
export class IgnoreBaseHrefInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
if (request.url.startsWith('/')) {
request = request.clone({
url: `${location.origin}${request.url}`,
});
}
return next.handle(request);
}
}
location.href/replace
to use absolute URLs (prepend location.origin
). Without it browser will prepend <base href>
to all of them and redirect user to CDN.Frankly speaking, all this looks like a very doubtful alternative to a simple --deploy-url
option and I would definitely vote for leaving it for ng build
and returning it back for ng serve
.
After reading the comment of @alan-agius4 again. He said following.
the
deployUrl
, is used not just to set thepublicPath
, usingdeployUrl
will cause additional steps including processing all CSS templates and files to append thedeployUrl
to the assetsurls
.
How many people here that need --deployUrl
to make angular application functional on CDN care about assets? Personally I don't.
To be clear, I host all the assets directly on CDN, not within the repository to be deployed through assets. So, for my scenario, --deployUrl
only means for CSS, JS bundles. Not for image, fonts, SVG or any other assets.
I do care
)I do care
)don't care
): For most of people that uses either Rails, ASP.NET or other to power the index.html, we normally have link directly to the fonts we need.don't care
): If host in CDN instead of from assets folder, we can include full link in CSS, or create Angular directive to use relative URL to translate to CDN link. Not an problem.don't care
): Same as image, or we could use approach similar to Font Awesome, save definition in TS and load it through component.So, alternative approach is to introduce another parameter to allow deploy URL only for JS, CSS and templates, ignore assets. That way, Angular CLI does not have to process anything related to assets, nor it will have much burden of package size.
@th0r expanded what @alan-agius4 have suggest to use base-url
to get around. Which is great, but it also reveal many hacks that need to be done. Base URL will cause router to broke, cause XHR/fetch to broke on relative URL. Agree with @th0r, I wouldn't count that as solution either.
I would try to provide few solution as well, some of them require angular team's implementation, which I will list here as potential get around.
--deploy-url
. It only care about lazy load. It will resolve any template, CSS, JS that's related to lazy loading and ignore all assets, such as font, image, SVG etc. (Depending on Angular team)
--deploy-url
for assets will suffer.APP_BASE_HREF
. Where we can define an CDN root path for lazy loading module to use. (Depending on Angular team)
--deploy-url
and less bundle size.Route("app/v2/{*appCacheAll}")
. What we can do is to detect income request, if it ends with .js
or .css
. We can respond with redirect to CDN response rather than content of index.html
.
.js
or .css
request, instead of relocated response, server act as proxy to assets.
That's all the solutions I can think of. First two solution depends on Angular team, where last 2 require backend skill. Which is what I am familiar with. Add @th0r's solution on top of it. Which is five different ways. So, if Angular team removed without introduce alternative way such as 1st and 2nd solution, we have at least one front end solution and two back-end solution.
This will cause us a lot of discomfort as we rely on deployUrl
heavily while building our production bundle.
We set the value as deployUrl: https://some-cdn.com/
where are JS and CSS artifacts are stored. Our baseHref
is completely different from the deployUrl
value.
Coming up with alternatives to this will be complex and will involve some tradeoffs (be it writing a script for the postbuild
option or using fileReplacements
to replace the .html
to name a few).
As a work around for the WebPack
publicPath
value you can set the global__webpack_public_path__
variable in your angular entry point. I have the following in mymain.ts
file.declare var __webpack_public_path__: string; __webpack_public_path__ = 'valueFormerlyAssignedUsing_deployUrl';
If you have different build configurations you can store the appropriate value in the
'./environments/environment
file and have those be replaced according to theangular.json
configuration.I do want to upvote this question though because it is a breaking change without any angular specific work around. We also use asp.net mvc to host the root file (instead of the angular index.html file) so the angular distribution folder is no the same as the load point url that asp.net uses. This means that anything dynamically loaded with
import
will fail because it can't be resolved.
@IgorWolbers didn't work for me though.
// main.ts
declare var __webpack_public_path__: string;
if (environment.production) {
__webpack_public_path__ = 'https://some-cdn.com/';
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
after running ng build
, we get:
<script src="runtime.71b88e53f069c658.js"
instead of <script src="https://some-cdn.com/runtime.71b88e53f069c658.js"
@aashwath - that is expected as this is for runtime behavior not for build time behavior (generation of the index.html file). If your index.html
, or whatever .html file you are using to load angular, is not in the same location as the angular javascript output files then you need to update that .html
file accordingly. AFAIK none of the proposed work arounds will do that for you automatically. All I can think of is a custom gulp script but maybe there is a better solution I am unaware of.
I suggest angular official dev team to keep deployUrl
option, becasue baseHref
and deployUrl
are not same usage to work, there's no reason to suggest one to become the replacement of another.
I really don't see why this was forced on the build. Listen guys, not everyone hosts the Angular way. There's nothing wrong with keeping the deployUrl option for those of us who need it. It doesn't play well with ng serve but that's fine, I just don't use ng serve. Don't make me hardcode the deployUrl all over the place to get this thing functional again.
I'll add to the list of replies, please don't deprecate/remove this option. Our application run from a .Net MVC razr view with assets hosted elsewhere, so the baseHref for routing is separate from what deployUrl does. You should assume having the base html page / routing / api calls independent from the assets loading calls is a pretty common use case, and module federation might become a pretty common one too.
I also need the deployUrl for AWS CDN of assets and baseHref to do the routing (i have a fun naming: https://myurl.com/with/team/avengers) ... so the app is hosted on "/with", and use "baseHref": "/with/" in production. My assets are not on "/with" .. but on "https://cdn.myurl.com/"
As a work around for the WebPack
publicPath
value you can set the global__webpack_public_path__
variable in your angular entry point. I have the following in mymain.ts
file.declare var __webpack_public_path__: string; __webpack_public_path__ = 'valueFormerlyAssignedUsing_deployUrl';
I'm on Version 13
same problem here: 'test' was not added to the build html paths
declare var __webpack_public_path__: string;
__webpack_public_path__ = 'test';
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import 'hammerjs';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
"@angular-devkit/build-angular": "~13.2.5",
` "@angular/cli": "~13.2.5",
"@angular/compiler-cli": "~13.2.4",
It seems that so many use cases are in favor of keeping the deployUrl option. I don't know if it would be possible for the Angular team , to enable/disable to use of deployUrl in configuration so that developpers who want it can opt-in for build performance gains and developpers who need the current behavior can keep using it ?
@alan-agius4 Any hope? 🙏 😅
I also need deploy URL, otherwise the chunks are not loaded correctly.
APP_BASE_HREF is not the solution.
This causes problems with our setup too. We use Spring Boot to serve the Angular app. The app is being built under resources/static/dist
. Spring by default serves content from the resources/static
folder, we use a subfolder so that we can restrict access to resources but whitelist the Angular app. We set the deployUrl to dist/
to make this setup work. The index.html is served as a Thymeleaf template with the base href set to the context path of the server, we do not use the index.html generated during build.
E.g. suppose the server runs on http://my-domain.com/foo
then we set the base href to /foo/
, the app resources are available at http://my-domain.com/foo/dist
.
With the deprecation of deployUrl we need to set the base href to /foo/dist/
for the assets to be loaded but this results in API requests going to /foo/dist/api
whereas we need them to go to /foo/api
.
Obviously there are ways around it (the best one currently being using deployUrl while we still can), but I would like to know what an elegant solution to this problem could be.
We also have problems with the deprecation of deployUrl
.
Our server serves two Angular applications from the same path depending on whether the user is authenticated or not, with the help of deployUrl
, we could set each application's assets paths to separate them on the server. In this case if user load http://example.foo/
and they are not authenticated, the server can serve lets say LoginApplication
which has deployUrl
set to assets/login/
, once they are authenticated the server serves lets say DashboardApplication
which has deployUrl
set to assets/dashboard/
.
APP_BASE_HREF
does not help us because it changes the browser URL to assets/login/
or assets/dashboard/
.
Any idea how this can be handled without deployUrl
?
How to use deployUrl
correctly and what problems it is good / not good for has been a difficult area for the community. We've done our best to assist folks where possible, but this is a big enough issue that our attempts to do so haven't really scaled effectively. After some discussion, we think the best path forward here is to update some of our docs to be more clear about various aspects and also write a blog post which discusses some common use cases where devs typically reach for deployUrl
and how our official recommendation for how to handle those requirements. That seems like the most scalable approach addressing the confusion around deployUrl
.
I'm closing this issue for now as we don't see anything directly actionable here, but hopefully better documentation and a future blog post will help clarify how best to meet your requirements.
@dgp1130 Could you clearly describe what will happen to deployUrl
? Will that property still pending on removal or Angular will keep that functionality.
People who posted here, or at least for me need an clear path of whether deployUrl
will be removed or not. Documentation and recommendation is good, but if the functionality will be removed, we need to plan ahead.
We are still planning to remove deployUrl
eventually largely because of all the confusion it causes. The exact version it gets dropped is still TBH, but I can confidently say it will not be removed in v14, so this shouldn't be an immediate problem for anyone. The blog post in particular should hopefully help address common use cases which have used deployUrl
in the past and how we recommend moving forward for each of them. We're not expecting any existing use cases to suddenly become unsupported, just that the right approach for you will probably vary by your particular requirements.
Had this same issue but found a solution that worked for me - https://stackoverflow.com/questions/71695674/what-is-best-way-to-go-about-replacing-deployurl-in-angular-json-for-v13/71913980#71913980
https://github.com/angular/angular-cli/blob/5402f99f8ad20e0a57456a416a992415fc6332bd/packages/angular_devkit/build_angular/src/webpack/configs/common.ts#L335