Skip to content

Dynamically import a stylesheet depending on the environment #4685

Closed

Description

@masaanli

Please provide us with the following information:

I'm having a question about how to dynamically import 'scss' files based on the environment which is serving or building.

Example.

I have a /styles/app/app-1.scss.
I have a /styles/app/app-2.scss.

Now i have also 2 environments called app-1 and app-2
I want to be able to say if it's served/builded environment is app-2 for example that it's loading the /styles/app/app-2.scss stylesheet.

This so I can develop 1 website and depending on how I build it's including the stylesheet for overwriting some parts of the application, style the application.

OS?

Windows 7, 8 or 10. Linux (which distribution). Mac OSX (Yosemite? El Capitan?)
Mac OSX Sierra

Versions.

Please run ng --version. If there's nothing outputted, please run in a Terminal: node --version and paste the result here:
cli c30 and node v 7.5.0

Repro steps.

Was this an app that wasn't created using the CLI? What change did you do on your code? etc.

The log given by the failure.

Normally this include a stack trace and some more information.

Mention any other details that might be useful.


Thanks! We'll be in touch soon.

Activity

  1. agatsoh commented on Feb 14, 2017

    @agatsoh

    Similarly I want to know how to include .less stylesheet files in an angular2 cli project in the current scheme of things.
    It would also be nice if you include in the Wiki in what order are CSS files loaded.

    1. From angular-cli.json
    2. From styles.css
    3. styleUrls array inside the Component.
      Which stylesheets are applied first and which ones last and which stylesheets would override the others ?
  2. RicardoVaranda commented on Feb 14, 2017

    @RicardoVaranda
    Contributor

    @agatsoh I believe you already posted the order correctly.

    At start up it loads it from styles array in angular-cli.json which is where styles.css is loaded from lastly your specific stylesUrls within your components.

  3. RicardoVaranda commented on Feb 14, 2017

    @RicardoVaranda
    Contributor

    @agatsoh @masaanli I have found a solution for this, keep in mind this isn't exactly supported by the cli but rather a feature of Webpack.

    Example of app.component.ts:

    import {Component, OnInit, isDevMode} from '@angular/core';
    
    @Component({
      selector: 'app-root',
      template: '<h1>Testing :)</h1>',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements OnInit {
      ngOnInit(){
        if(isDevMode()) {
          require("style-loader!./../styles2.css");
        } else {
          require("style-loader!./../styles.css");
        }
      }
    }
    

    Where styles2:

    h1 {
      color: red;
    }
    

    Styles:

    h1 {
      color: green;
    }
    

    While this may work it was brought to my attention by @filipesilva that there might be some drawbacks to using this solution such as:

    • Your code not being as portable
    • Styles may not be processed correctly
    • Might break if cli changes its loaders
  4. masaanli commented on Feb 14, 2017

    @masaanli
    Author

    @RicardoVaranda nice ! Something like this was what I searched for.

    But then it's loading a css file. But is there maybe a option to do it when building or serving the project? So it's scss compiled together?

  5. RicardoVaranda commented on Feb 14, 2017

    @RicardoVaranda
    Contributor

    @masaanli Similar to how I have stated above

    if(isDevMode()) {
          require("style-loader!./../styles2.css");
        } else {
          require("style-loader!./../styles.css");
        }
    

    You can also do:

    if(isDevMode()) {
          require("style-loader!./../styles2.scss");
        } else {
          require("style-loader!./../styles.scss");
        }
    

    Where for example styles2.scss is:

    $green : green;
    
    h1 {
      color: $green;
    }
    

    But once again the same implications I stated above apply.

  6. masaanli commented on Feb 14, 2017

    @masaanli
    Author

    @RicardoVaranda

    I tested it and it is working!

    The problem I discovered is, I use Bootstrap and have in the angular-cli.json my variables defined.

    I cannot override them in the app.component (require .scss)

    You know maybe a solution for this?

    I managed to fix it, but maybe there is abetter solution.

    I fixed it by @import everything in that stylesheet.

    so i require a stylesheet dynamically in the app.component.ts and in that stylesheet i import the other stylesheets. (so i have double code) for several apps. but it's working.

    STILL HAVE THEN THE PROBLEM: That the variables inside the style file for components which are included like 'styleUrl' are not working anymore!!

    (because 2 times require will not work. (cannot read variables from the other files).

  7. RicardoVaranda commented on Feb 15, 2017

    @RicardoVaranda
    Contributor

    That is because the instance of style-loader is different when called by the cli and when It's called by the require and thus you won't have access to variables from the cli instance and vice versa.

    To be honest I think you are over complicating the logic to do this, A better approach for you in my opinion would be to have 2 different styles: styles.scss styles-dev.scss

    Inside each of these just do the @import like you mentioned above requiring any other file you need to overwrite. Yes you will have change it in angular cli every time you want to switch between environments but realistically how often are you really switching between both of these.

    If you wanted to change environments you would have to turn off the cli and re-run it with the new environment variable anyway with your current setup.

  8. masaanli commented on Feb 15, 2017

    @masaanli
    Author

    @RicardoVaranda true, but I have 1 code base and depending on the commando's I wanna be able to say deploy this version or deploy that version. You cannot add logic in the angular-cli json ;) So that's no option for me.

  9. agatsoh commented on Feb 15, 2017

    @agatsoh

    @RicardoVaranda Do you have any idea how we import .less stylesheets in angular2 ?

    And I apologise I forgot to ask you, what about the stylesheets declared in index.html ? Do they override all other stylesheets? This is what I am observing.

  10. filipesilva commented on Feb 15, 2017

    @filipesilva
    Contributor

    Closing as this is now mostly a discussion question instead of an issue.

    The CLI doesn't provide you with any preferred way out of the box to dynamically lazy load styles and apply then. You'll have to come up with a strategy yourself. Stack Overflow has great answers for this sort of question.

    Functionality that we do have is a way to have a global style that isn't loaded by default in index.html, and instead are just outputted to dist. You can set a global style to be lazy, see https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/global-styles.md for more info. This has actually been around for a while but wasn't documented.

    Here are a couple of random web results that show how you apply styles via javascript:

    Approaches that intrinsically depend on webpack loader might work right now but are not officially supported, which means they can stop working at any time or might not work as expected.

  11. RicardoVaranda commented on Feb 15, 2017

    @RicardoVaranda
    Contributor

    @agatsoh Depends on where you want to import them and how. If you mean dynamically like what masaanli was trying to achieve you can use the same solution as I posted above I've tested it by refactoring styles2.scss to be styles2.less and the contents to be:

    @green : green;
    
    h1 {
      color: @green;
    }
    

    This will load correctly but it's prune to the drawbacks I have posted above.

    The priority in which it applies the css is as follows:

    Component -> angular-cli styles [] -> index.html

    This screenshot might clarify it for you:
    image

    Blue is the component styles, red is styles.css being loaded from angular-cli.json and finally green is a link tag from index.html.

    Hope this clarifies your queries.

  12. kujma10-zz commented on Jul 26, 2017

    @kujma10-zz

    I have used solution from this issue. However when isDevMode is undefined, it loads both scss files. When I console.log it only goes to the second block. Any idea why this happens?

        if(isDevMode()) {
          require("style-loader!./../styles2.scss");
        } else {
          require("style-loader!./../styles.scss");
        }
    
  13. yasiryc commented on Jul 26, 2017

    @yasiryc

    @kujma10 , did you import isDevMode from @angular/core? Also ensure that you are doing development build and not production.

  14. sandangel commented on Oct 12, 2017

    @sandangel

    I have the same question but different use case. My deploy page is https://username.gitlab.io/admin/ with /admin suffix while development mode is just localhost. I had to use route hash strategy because gitlab not support serve index.html with all route, then I had to create 2 same scss file in my home component, home.component.scss and home.prod.component.scss because the img url in dev mode is differ with prod mode (/assets/img/img.jpg and /admin/assets/img/img.jpg). I tried dynamic load with environment.production but when I run ng serve, it has an error not found home.component.scss. but it work in ng serve --aot and ng build -prod.

    @angular/cli: 1.5.0-beta.4
    node: 8.7.0
    os: linux x64
    @angular/animations: 5.0.0-rc.1
    @angular/cdk: 2.0.0-beta.12
    @angular/cli: 1.5.0-beta.4
    @angular/common: 5.0.0-rc.1
    @angular/compiler: 5.0.0-rc.1
    @angular/core: 5.0.0-rc.1
    @angular/flex-layout: 2.0.0-beta.9-c3c7151
    @angular/forms: 5.0.0-rc.1
    @angular/http: 5.0.0-rc.1
    @angular/material: 2.0.0-beta.12
    @angular/platform-browser: 5.0.0-rc.1
    @angular/platform-browser-dynamic: 5.0.0-rc.1
    @angular/platform-server: 5.0.0-rc.1
    @angular/router: 5.0.0-rc.1
    @angular/compiler-cli: 5.0.0-rc.1
    @angular/language-service: 5.0.0-rc.1
    typescript: 2.5.3
  15. andre-f-paggi commented on Oct 20, 2017

    @andre-f-paggi

    Any other workarounds or a solution for this?

  16. matiishyn commented on Nov 11, 2017

    @matiishyn

    Trying to load SCSS file in my TS files gives me an error while build:
    Cannot find name 'require'

  17. mousavidev commented on Jan 26, 2018

    @mousavidev

    I am looking for such a solution.

    theme.ts

    interface Theme {
      url: string;
      rel: string;
      media: string;
    }
    

    app.component.ts

    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      theme: AppComponentThemeProvider
    })
    export class AppComponent {
      constructor() {
      }
    }
    

    app.component.theme.ts

    export class AppComponentThemeProvider {
      constructor(@Inject(LOCALE_ID) private localeId) {
      }
    
      getThemes() {
        let themes = [<Theme>{url:'./app.component.css'}];
    
        if(this.localeId == 'fa-IR') {
          themes.push(<Theme>{url: './app.component.rtl.css'})
        }
    
        return themes;
      }
    }
    
  18. ghiscoding commented on Jul 26, 2019

    @ghiscoding

    @matiishyn
    You just need to add a Type for the require method, you can also add it to your Global Types if you have (maybe global.d.ts) or directly in your code like so

    declare function require(name: string): any;
    require('style-loader!./custom-styles.scss');
  19. angular-automatic-lock-bot commented on Sep 9, 2019

    @angular-automatic-lock-bot

    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.

  20. locked and limited conversation to collaborators on Sep 9, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions