Mac OS X
node: 6.9.1
os: darwin x64
Due to the way that assets are built, references to images in html templates are not processed by webpack and therefore aren't fingerprinted. Furthermore, the src
attributes don't get prefixed by publicPath
(in the event that the PR #3285 is merged) and we don't have the flexibility to locate images with their components (#3292)
n/a
I have a branch that uses html-loader
instead of raw-loader
for html files, which fixes all these problems. I'd be willing to create a PR that makes this change, perhaps only if passed a config option, but I'm open to other ideas and would like to get the opinion of the angular-cli team as to whether this is something they're interested in supporting.
The problem is that html-loader
is not Angular2 friendly. It messes up the paths and doesn't understand the templates. We're looking at it but had to remove it (see https://github.com/angular/angular-cli/pull/2537 and all related issues). We need more investigation.
Oh and for reference, the assets path being wrong is https://github.com/webpack/html-loader/issues/85.
@hansl I'm not sure what you mean by messing up paths? You can get html-loader
to play nicely with Angular 2 by passing it a -minimize
flag, I have it working just fine in a non-angular-cli app, and I've hacked in support for it in our custom angular-cli branch. It supports the current use of the assets folder by prefixing with ~
, so <img src="~assets/images/foo.png">
will be interpreted as require('assets/images/foo.png')
while <img src="assets/images/foo.png">
will be interpreted as require('./assets/images/foo.png')
.
I'm a bit confused though, what was the issue with base href exactly?
The change would essentially break all existing projects that use img
tags. The use of the ~
is also webpack specific so institutionalizing its use would break down the CLI's abstraction around webpack.
Looking at the documentation, the html-loader does seem to have a setting named root
, which could be set to the project root and allow root-relative URLs to be used instead of the ~
. Whether that is better or not is up to question (e.g., confusion with regards to base href usage). Also, its viability would need to be tested of course; and it would still be a breaking change.
@clydin At first it should probably be opt-in, though I'd love to find a way to make it backwards compatible with existing projects. To your point yes, ~
is webpack-specific but it's something that already works in CSS @import
statements: @import "~assets/stylesheets/variables.scss";
.
It seems like there's a bit of a philosophical split here with regard to how assets are resolved- in most cases (TypeScript files, CSS, etc) they're hooked into the Webpack system and resolved on-disk at compile time, but in templates they're referred to by relative path.
For what it's worth, at my company we serve all our assets (JS and otherwise) from a CDN, and only serve the template from our primary domain. By setting publicPath
in Webpack, our configuration works out of the box in both development and prod, plus we get fingerprinting (and even inlining if we want) of our images for free.
The only way to support this use case with the current incarnation of angular-cli is to manually prefix every src
attribute with our CDN URL which is a non-starter. I suppose we could write a build task that does string replacement on the built artifacts but that still doesn't get us 1) cache busting and 2) the ability to refer to images outside the assets
folder.
I'm open to other ways of solving this problem and I'm certainly sensitive to the backward-compatibility concerns. Perhaps a custom html loader that processes paths differently, eg src="assets/images/foo.png"
is the equivalent of require('assets/images/foo.png')
and src="./images/foo.png"
is equivalent of require('./images/foo.png')
?
@texel, I agree as to the value it would provide. Implementing it with minimal upheaval is the problem.
As to the use of the tilde, yes it currently works in other areas (as do several other webpack/webpack-plugin related capabilities), but they are currently not (at least to my knowledge) documented as supported or recommended features of the CLI. Personally, I view them no different than internal APIs: They work but expect them to break in the future.
are there any updates on this issue?
+1
Any update on this issue? Also see #4212
We had this in once, by using HTML-loader
, but it broke a lot of projects (see https://github.com/angular/angular-cli/issues/2396).
Is it not possible to have a parameter for disabled the control on url?
Like css-loader?url=false
I think this request is a common requirement, in fact, I have been using my customized version, and never put any file in the asset directory, can provide an option to allow users to choose which way to use?
Thank you.
https://github.com/angular/angular-cli/issues/8139 is a duplicate of this issue, but there's really good insights there onto why this is hard.
@asnowwolf it's great to know that you've come up with a solution that fits your usecase. I am interested in how you did it. Would you be interested in making a rough PR and we can see if we can take it from there? Don't worry too much about making it look good for now, just the basic logic to see if there's any important thing missing.
Ok, thank you. I will be about to submit a PR next week. The basic idea is to add an option in .angular-cli.json to identify the processing strategy used in this project.
@filipesilva
I have just found a new way that may be able to gracefully solve this issue, see the PR https://github.com/angular/angular-cli/pull/8190
But I could not fully understand the issue described in #8139, my current understanding is that HTML-Loader does not support the template with angular directives and binding syntax, e.g. <img src="{{id}}.jpg"/>
, but the specific situation I do not know. Is there a demo project that can reproduce this issue?
@filipesilva - Ping me in slack when you have some time to discuss. I would also prefer to come up with a general solution that solves this issue for more than the specific use case mentioned.
@filipesilva is there any update or news for this issue?
It is really troubling that a basic, fundamental need like this remains unresolved after over 12 months. The entire 'new world' in angular is for everything to be built around components. However, if the components aren't self-contained (images/assets/etc. owned by the component, not the project), then I don't understand how components can be easily re-used between projects.
Perhaps on a larger scale this issue also relates to the discussion around the angular package format and how components are meant to be distributed/referenced by multiple projects.
Regardless, unless people follow the monorepo/monoproject approach, I don't see a simple path forward for sharing components at this point.
It's been a long migration for me over to Angular from AngularJS, and I am hoping some of these features will hit the horizon. Subbed here
In order to fix this issue, I usually add declare var require: (path: string) => any
to src/typings.d.ts
to fix missing typing and I create component properties pointing to the images:
@Component({
template: `<img [src]="logoUrl">`
})
export class LogoComponent {
logoUrl = require('./logo.png')
}
As angular 6 is now final and public: how is this solved there? Can you provide a back port to NG5 to prevent lots of @yjaaidi solution(s)??
any solution for adding hash to images files or other type of files in assets folder? any updates on this?
By the way, I wrote a blog post about this.
https://blog.wishtack.com/2018/06/20/unleash-the-power-of-webpack-in-angular/
The nice thing is that you end up with build errors if the picture is not there plus it works with any kind of file.
Have a nice day!
@texel this is out of our roadmap. @yjaaidi's blog post describes a possible solution although it may have some performance implications, and introduce extra boilerplates.
I'd recommend you to look at ngx-build-plus, so you can introduce a custom webpack config.
@mgechev I'm kindly asking you to revisit this decision. I was shocked that Angular CLI doesn't support such a common and a must-have use case like rewriting asset URLs in component templates! What is the recommended approach to refer images from the components then if I want to upload all of them to the CDN? Hash all of them manually, write the names map to the manifest.json
, send it to the client and use some helper/pipe to rewrite them in the runtime? But this solution doesn't scale and require a lot of manual work.
If you need my input, we can discuss when I'm back in the office.
I think we needn't provide a solution that works for "anywhere, any file". It only needs to support the most common situations and avoid introducing side effects.
For example, the <img src="./some-file.png">
(common) can be directly supported, while <img src="./some-{{files}}.png">
(not common) can prompt the user to change to <img [src]="file">
, and provide a utility function loadAsset()
(essentially require()
) allows the user to call in the component. And also, for backward compatibility, only add the fingerprint to the relative path starting with ./
, and the absolute path starting with /
uses the original way, and even further: Use the original way for the files in the assets
section of angular.json
.
What would an alternative approach using ngx-build-plus
look like? Has anyone tried this yet and is willing to share his/her knowledge?
After looking at this issue again today and discussing it for a while, we think that it's a good candidate for a third-party plugin that can be integrated in a CLI project using ngx-build-plus
.
Our main concern is that a lot of the asset references in templates are dynamic. In a dataset of over 1,500 project we found that over 34% of the bindings to an src
attribute of an image are dynamic. Unfortunately, dynamic bindings cannot be resolved at build time. Imagine the scenario:
@Component({
template: '<img [src]="foo">'
})
class ImgComponent {
foo: string = localStorage.get('image');
}
It is impossible at build time to know the value of the src
attribute.
It's possible, however, to handle part of the use cases (the other 64% from our dataset). This is a great candidate for a third-party plugin that you can use with the CLI. If anyone is willing to contribute, feel free to share an insight in this issue.
If the plugin satisfies most of our users' needs for adding a hash in the name of static assets referenced in templates, we'll be happy to reconsider and introduce it as part of the CLI core.
@mgechev but how do you propose to solve this issue?
Possible third-party solution is a webpack plugin which parses the component template, discovers all the statically referenced resources, calculates their hash and appends it to their names.
Alternatively, now, you can take advantage of the static nature of CSS and use classes with background images, instead of <img>
elements.
Another solution would be to control your caching policy or introduce a service worker that takes care of that.
Few sample solutions on top of my head.
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
_This action has been performed automatically by a bot._
Most helpful comment
It is really troubling that a basic, fundamental need like this remains unresolved after over 12 months. The entire 'new world' in angular is for everything to be built around components. However, if the components aren't self-contained (images/assets/etc. owned by the component, not the project), then I don't understand how components can be easily re-used between projects.
Perhaps on a larger scale this issue also relates to the discussion around the angular package format and how components are meant to be distributed/referenced by multiple projects.
Regardless, unless people follow the monorepo/monoproject approach, I don't see a simple path forward for sharing components at this point.