Skip to content

How to lazy load global style? #5880

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

Closed
calloncampbell opened this issue Apr 7, 2017 · 19 comments
Closed

How to lazy load global style? #5880

calloncampbell opened this issue Apr 7, 2017 · 19 comments

Comments

@calloncampbell
Copy link

calloncampbell commented Apr 7, 2017

Bug Report or Feature Request (mark with an x)

- [X] bug report -> please search issues before submitting
- [ ] feature request

Versions.

@angular/cli: 1.0.0
node: 6.9.2
os: win32 x64
@angular/animations: 4.0.1
@angular/common: 4.0.1
@angular/compiler: 4.0.1
@angular/core: 4.0.1
@angular/forms: 4.0.1
@angular/http: 4.0.1
@angular/platform-browser: 4.0.1
@angular/platform-browser-dynamic: 4.0.1
@angular/router: 4.0.1
@angular/upgrade: 4.0.1
@angular/cli: 1.0.0
@angular/compiler-cli: 4.0.1

Repro steps.

  1. Read the following: https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/global-styles.md
  2. In my .angular-cli.json file, add the following to the styles array:
    "styles": [
    "scss/style.scss",
    { "input": "scss/style.client-1.scss", "lazy": true },
    { "input": "scss/style.client-2.scss", "lazy": true }
    ],
  3. What is next step to load either of these styles at runtime?

Desired functionality.

I need to provide different global styles for certain customers to my website and once they login, override certain bootstrap variables (colors, logo, etc). I have defined some client specific SCSS files and marked them as lazy load in the styles array.

Now I need to load them...help!

Docs in the link below mention "You can also rename the output and lazy load it by using the object format" - so how do I load them?

Mention any other details that might be useful.

https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/global-styles.md

@vkovacev
Copy link

vkovacev commented Apr 8, 2017

When looking at the cli output, the lazy loaded files are generated, so with:

"styles": [
        {"input": "../test-style.scss", "lazy": true, "output": "test-style"}
],

There will be a test-style.bundle.js generated:

chunk {4} test-style.bundle.js, test-style.bundle.js.map (test-style) 9.88 kB {7} [initial]

However I didn't really manage to find a civil way of importing that bundle withing the app code. The bundle can be imported in the index.html via script tag, but importing it before inline.bundle.js will result in webpack error. Importing it afterwards will work - this can be done from the app code by appending the script tag with the bundle output name:

function loadScript(scriptUrl) {
  const script = document.createElement('script');
  script.src = scriptUrl;
  document.body.appendChild(script);
}

loadScript('test-style.bundle.js');

This will load the styles, but it is hardly an ideal solution. I as well would really like to know a proper way to load lazily both styles and scripts (as this is applicable to scripts as well). It should also be added to the docs I think.

@intellix
Copy link
Contributor

intellix commented Apr 8, 2017

Just tried this too. I Copy/Pasted styles.scss and called it lazy.scss then added it like in the story.

  • /styles.bundle.js // this is added eagerly
  • /lazy.bundle.js // I can add this manually to index.html or load it programmatically

@a5hik
Copy link

a5hik commented Apr 14, 2017

It would be great If someone describes .. how to programatically refer the lazy generated Script / Styles at runtime.. the bundles are generated at build time.. but couldn't find a proper way to load at it runtime.

@delasteve
Copy link
Contributor

@a5hik unfortunately the CLI doesn't support programmatically adding lazy scripts and styles because there isn't a way to tell when it should be loaded. You will have to do that yourself.

@a5hik
Copy link

a5hik commented Apr 27, 2017

I actually ended up moving all the global scripts and styles to corresponding modules.. wanted to load everything lazily..

@sumitarora
Copy link
Contributor

Closing as above.

@aaronfrost
Copy link

aaronfrost commented Jun 13, 2018

I don't know why this was closed. The question still very much remains.

When I lazy load a css file, the production build process output includes a chuck hash that I can't know, making it impossible to know in my code the name of the file. Let me show. If I add the following to my angular-cli.json:

    "styles" : [
        { "input" : "foo.css", "lazy" : true }
    ]

then the build will output a file called foo.<some random hash>.bundle.js.

Because the file name is generated and not static, I can't lazy load it. How do I get either the name to be static... or what is the prescribed way to do this by the Angular CLI team? I have the ability to create the lazy css file. How do I use it?

@filipesilva ^^^

@pycraft114
Copy link

@filipesilva any idea about this?
Are there any specific documentation about how to properly use globally lazy loaded scripts?
https://github.com/angular/angular-cli/wiki/stories-global-scripts
docs written here is kinda vague that its hard to know how to properly use in the "real world"

@rafagsiqueira
Copy link

@aaronfrost, use the option bundleName with your lazy loaded style:

"styles": [
              {
                "input": "lazy-style.scss",
                "lazy": true,
                "bundleName": "lazy-style"
              }
            ],

Then angular compilation will generate a bundle named lazy-style.js. You can then lazy load it somewhere like this:

const element = document.createElement('script');
element.src = 'lazy-style.js';
document.body.appendChild(element);

@miloskovacevic
Copy link

miloskovacevic commented Mar 7, 2019

@rafagsiqueira i did everything that you did, then run npm start , but i don't see my bundle.
{
"input":"../src/assets/workflowengine/js/workflowdesigner.min.js",
"lazy":true,
"output": "lazy-wfdesigner"
}
I see all others bundles loaded in index.html which is fine. Where to look for lazy bundle?
edit: i tried also with "bundleName" instead of "output", result is the same

@madmurl0c
Copy link

This is what I use to create my lazy style bundle

{
    "input": "src/styles/lazy-styles.scss",
    "lazy": true,
    "bundleName": "lazy-style"
}

This is how I load the bundle in my app.component.ts' constructor

// Add lazy loaded css
if (environment.production) {
  const element = document.createElement('link');
  element.rel = 'stylesheet';
  /* Only works as stylesheet but not with preload
  element.rel = 'preload';
  element.as = 'style';
  */
  element.href = 'lazy-style.css';
  document.body.appendChild(element);
} else {
  const element = document.createElement('script');
  element.src = 'lazy-style.js';
  document.body.appendChild(element);
}

Why is there a .js file in development mode and it compiles to a .css file in production? Is there any reason for this or did I just configure it the wrong way?

@filipesilva
Copy link
Contributor

@madmurl0c the JS file in dev builds is for faster CSS rebuild. You can make it always be css with --extractCss=true. If you set this on the build options in angular.json, it will always apply.

@madmurl0c
Copy link

@filipesilva Thanks, extractCss did the trick :)

For anyone having issues with this, the following works fine for me:

angular.json

"styles": [
    "src/styles/styles.scss",
    {
        "input": "src/styles/lazy-styles.scss",
        "lazy": true,
        "bundleName": "lazy-style"
    }
],
"extractCss": true,

constructor in app.component.ts

const preloadElement = document.createElement('link');
preloadElement.rel = 'preload';
preloadElement.as = 'style';
preloadElement.href = 'lazy-style.css';
document.body.appendChild(preloadElement);

const lazyStyleElement = document.createElement('link');
lazyStyleElement.rel = 'stylesheet';
lazyStyleElement.href = 'lazy-style.css';
document.body.appendChild(lazyStyleElement);

@johanchouquet
Copy link

Hi,

What's bothering me here with this approach, is that we do access DOM directly and not using abstraction. What should be the prefered way to do so ?

One other question: let's say i change my angular.json file to add { "input" : "myUrl", "lazy": true, "bundleName": "myBundle"} in the Builder configuration. This applies only for ng build , right ?
But what if i want those exact same file in my dev process, by doing ng serve ? For the dev process, it seems to me that I can't have a way to lazy load those same files.

Or am i missing something ?

Maybe I should open another issue ? I couldn't find any relevant information about this on the web & docs.

@filipesilva
Copy link
Contributor

@johanchouquet neither Angular or Angular CLI provide an abstraction for lazy loading CSS. Angular CLI just creates CSS bundles that aren't automatically loaded. I don't think that Angular itself needs to provide an abstraction for it either, mostly because it's not Angular specific.

Since ng serve actually uses the build configuration, both will have the lazy CSS bundle.

@johanchouquet
Copy link

@johanchouquet neither Angular or Angular CLI provide an abstraction for lazy loading CSS. Angular CLI just creates CSS bundles that aren't automatically loaded. I don't think that Angular itself needs to provide an abstraction for it either, mostly because it's not Angular specific.

Since ng serve actually uses the build configuration, both will have the lazy CSS bundle.

Thanks @filipesilva . That's clear for me for ng serve and ng build 👍 !

About the abstraction in Angular, I understand that the CLI provides the starting solution and this part of the job is done i believe. But I'm more interested in the 'How to use the JS scripts that have been lazy loaded' part ?

Because, to me, this solution would work only in a browser environment, right ? So what about server side, or in web worker etc ... ? Shouldn't we take car of this using for example Renderer2 at the very least ? But even doing so, we still would have to interact with the nativeElement and call some method to create the nodes. And that's what i think is discouraged by the Angular team.
Again, this may not be the best place to talk about it, but sure where it'd be better. Sorry for that.

@filipesilva
Copy link
Contributor

Personally I haven't given much thought about how to use lazy loaded js/css in a server environment. But the default builder in Angular CLI is only mean to build browser apps, so I don't think that's something that should be covered there. The folks at https://github.com/angular/universal, who cover the server journey, would know more.

@johanchouquet
Copy link

@filipesilva, this is true indeed. So, maybe this is not a big deal. I was just wondering about code portability there. So this makes sense to do inject those scripts in the DOM directly.

@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 Sep 9, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests