Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assets, base href and relative urls in css/scss #17747

Closed
kunajs opened this issue May 18, 2020 · 4 comments
Closed

Assets, base href and relative urls in css/scss #17747

kunajs opened this issue May 18, 2020 · 4 comments

Comments

@kunajs
Copy link

kunajs commented May 18, 2020

First of all, my project has multiple localizations defined in angular.json, so when running
ng build --configuration=production --localize
there are 3 subdirectories created in dist output path. Each of them has different base href in their index.html file.

Second, in angular.json I have assets subsection defined like:

"assets": [
  "src/favicon.ico",
  "src/assets"
],

and in assets/images there are these image files (among others):

  • bg_login_compressed.jpg (blured version of image, small size)
  • bg_login.jpg (full version of image, large size)

Then I have a component in some nested directory within the project, that is using following css classes:

.background-init {
  background-image: url("/assets/images/bg_login_compressed.jpg");
}
.background-loaded {
  background-image: url("/assets/images/bg_login.jpg");
}

As you can see, I am using root path to assets folder (starting with '/'), which is obviously wrong for base href that is not '/'.
My goal is to have div initialized with blured version of image, which is later replaced by full version. This is how I am doing it:
Template:

<div  [class.background-init]="!backgroundIsLoaded" [class.background-loaded]="backgroundIsLoaded">

Typescript:

public backgroundIsLoaded = false;

ngOnInit() {
  const img = new Image();
  img.src = './assets/images/bg_login.jpg';

  img.onload = () => {
    this.backgroundIsLoaded= true;
  };
}

This is ok during local development when running ng serve without base href parameter:

  • .css images are found
  • .ts image is found and loaded
  • both .css and .ts bg_login.jpg file is searched in the same path (= refer to the same file)

If I run ng serve --base-href /de/, which simulates run from production environment on IIS:

  • .css images are not found (they are searched in path '/assets/images/' instead of '/de/assets/images/')
  • .ts image is found and loaded
  • as a result, no images are shown as the background

Ok: let's switch to relative paths in .css url path, as advised in #4778 (comment):

.background-init {
  background-image: url("../../../assets/images/bg_login_compressed.jpg");
}
.background-loaded {
  background-image: url("../../../assets/images/bg_login.jpg");
}

On local development without base href it is working fine, as well as with --base-href /de/.
BUT what this causes when building? The files used in relative paths are copied from /assets/images/ to root directory of each localized build. This is unwanted and inpractical for many reasons described e.g. here: #14587 (comment) !!!
Side effect of this solution is:

  • .css image bg_login.jpg is searched in root path (of the build)
  • .ts image bg_login.jpg is searched in /assets/images/ path
  • that's why the code for replacing the background images will not work as desired (the image is loaded twice instead of once on the same path)

Other solutions that I tried:

  • using of rebaseRootRelativeCssUrls: not working on production and considered deprecated
  • using tilde in the .css path, such as url('~/assets/images/bg_login.jpg'): not working on production
  • using caret in the .css path, such as url('^assets/images/bg_login.jpg'): not working on production and officially not supported (Can't use relative paths in url() in scss files #12797 (comment))

I am searching for a clean solution which:

  • finds the images in the paths
  • doesn't copy the images when using relative css paths
  • is able to link to the same file when loading both from css and from ts
  • works on local and on production environment

Unfortunately, many of similar issues here on git are already locked due to inactivity, that's why I am creating this new one.

🌍 Your Environment



Angular CLI: 9.0.6
Node: 12.16.0
OS: win32 x64

Angular: 9.0.6
... animations, cli, common, compiler, compiler-cli, core, forms
... language-service, localize, platform-browser
... platform-browser-dynamic, router
Ivy Workspace: Yes

Package                            Version
------------------------------------------------------------
@angular-devkit/architect          0.900.6
@angular-devkit/build-angular      0.900.6
@angular-devkit/build-ng-packagr   0.900.6
@angular-devkit/build-optimizer    0.900.6
@angular-devkit/build-webpack      0.900.6
@angular-devkit/core               9.0.6
@angular-devkit/schematics         9.0.6
@angular/cdk                       9.1.3
@ngtools/webpack                   9.0.6
@schematics/angular                9.0.6
@schematics/update                 0.900.6
ng-packagr                         9.0.3
rxjs                               6.5.4
typescript                         3.7.5
webpack                            4.41.2
@alan-agius4
Copy link
Collaborator

alan-agius4 commented May 28, 2020

Hi,

To follow up on some points mentioned in #14587 (comment)

  1. It does clutter the top-level folder on your server. Deployments should be performed by automatic tools, but you cannot always exclude a manual intervention on your server. If so, acting in a list of hundreds of files is risky.

You can use resourcesOutputPath, to avoid this. See: https://angular.io/cli/build

  1. It does duplicate the assets. If you do not want duplication, you should move your CSS-relatively-referenced files to a folder which is not copy/pasted as-is into production build like "assets" folder (assuming "assets" is the default name when generating a project). But these files are eventually renamed using a hash, so you cannot use them in non-CSS contexts, for instance HTML templates.

You can choose not to hash such files, using the outputHashing option. See: https://angular.io/cli/build

Due to the files being copy-pasted to the root output folder, multiple image files used by CSS can have conflicting names for local ng serve. Just add two different images into two different folder of the assets. Use them both in CSS with relative paths. Only one of them will be available due to conflicting name. Workaround : create an ng serve build configuration which is using the outputHash: 'media' similar to prod build.

This is indeed a bug and is being tracked #12186

Now, back to the your issue, currently there is no supported way to disable relative images referenced in css from being copy, however it's still fairly simple to achieve what you want.

  • Disable outputHashing for media. Use outputHashing: bundles.
  • Don't copy the assets folder. (This is redundant).
  • Set the resourcesOutputPath to assets/images.
  • Remove root paths

@alan-agius4
Copy link
Collaborator

alan-agius4 commented May 28, 2020

Additionally, I'd personally would avoid to go with the above approach from the start to lazy load an image.

IMHO, a more clean, non coupled and re-usable approach would be to create a directive that:

  • Takes 2 inputs full resolution image low resolution image
  • OnInit set the img src to the low-resolution version
  • Start loading the full resolution image in the background
  • On full resolution image load change the image

And if you are looking to improve the performance even further can can use IntersectionObserver to that you only load the images when they are in view.

@alan-agius4 alan-agius4 self-assigned this May 28, 2020
@alan-agius4
Copy link
Collaborator

Closing as the above explanations should suffice.

If the problem persists please open a new issue with a reproduction.

Thanks.

@angular-automatic-lock-bot
Copy link

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.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Jun 29, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants