Angular2: Custom material theme in each lazy loaded submodule

2019-02-12 angular angular-material2

Problem with loading custom Angular Material 2 themes in separate submodules of Angular 7 app

I have angular wrapper module (app module) and many independent lazy-loaded sub-modules. Each sub-module behaves like a totaly separate app and needs its own angular material theme.

I've created custom theme for each sub-module and tried to reference the custom theme in each of components' .scss file with relative path.

Some of the Material components apply the custom theme (button) and some (mat-form-field) just take the theme specified in styles.scss.

One way I can avoid this is including each custom theme in 'styles' array of angular.json, but I'm afraid that, by doing this, each independent sub-module (app) will have to load every themes' compiled .css and it'll be overhead.

Is there any known issue with this or any better solution?

home-app.component.scss:

@import './theme.scss';

VS.

angular.json

...
"styles": [
    "src/styles.scss",
    "src/app/orgs/home/theme.scss" // <-- FIXED BY ADDING THIS
],
...

Expected: Theme declared in the home-app.component.scss will be loaded and applied to every material component used in home-app.component.html

Actual: If I don't add "src/app/orgs/home/theme.scss" to angular.json, the custom theme declared in styles.scss will be applied to some material components used in home-app.component.html, and some material components will use the imported theme specified in home-app.component.scss

Answers

Did you try to add it directly into styles array of your Components?

@Component({
  selector: 'app-home',
  template: 'your-template.html',
  styles: ['your-style.scss', 'your-theme.scss']
})

Another thing you can do is to use ShadowDom as your ViewEncapsulation :

@Component({
  selector: 'app-home',
  template: 'your-template.html',
  styles: ['your-style.scss', 'your-theme.scss'],
  encapsulation: ViewEncapsulation.ShadowDom
})

Try to just add ng generate module Shared which will create SharedModule at the root right next to AppModule with nothing in it. For some reason when it recompiles it may reconnect some of your sub modules to a theme like Angular Material Themes magically. Not sure what going on under the hood.

I think i understood your problem and here is a solution for you :)

So when you create a new ng-project with the cli with opt in material-theming, you will get a inital theming configuration like the following (or you can find the condig in the Material Guides)

import '[email protected]/material/theming';
// Plus imports for other components in your app.

// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once!
@include mat-core();

// Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/
$candy-app-primary: mat-palette($mat-indigo);
$candy-app-accent:  mat-palette($mat-pink, A200, A100, A400);

// The warn palette is optional (defaults to red).
$candy-app-warn:    mat-palette($mat-red);

// Create the theme object (a Sass map containing all of the palettes).
$candy-app-theme: mat-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn);

// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
// that you are using.
@include angular-material-theme($candy-app-theme);

The most important part of this is to remberthat you must include just once the mat-core() in your application (in oredr to have single material styles css file).

After that you can create custtom themes by including import '[email protected]/material/theming'; whereve desired in the application and apply the theme by wrapping it in a css calss. For example after you include the mat-core once in you root .scss file, in a lazy loaded module you can do the following thing:

@import "[email protected]/material/theming";

.theme1 {
  $theme-primary: mat-palette($mat-red);
  $theme-accent: mat-palette($mat-lime, A200, A100, A400);

  // The warn palette is optional (defaults to red).
  $theme-warn: mat-palette($mat-red);

  // Create the theme object (a Sass map containing all of the palettes).
  $theme-theme: mat-light-theme($theme-primary, $theme-accent, $theme-warn);

  // Include theme styles for core and each component used in your app.
  // Alternatively, you can import and @include the theme mixins for each component
  // that you are using.
  @include angular-material-theme($theme-theme);
}
<div class="theme1">
  <mat-toolbar color="accent">
    <button color='primary' mat-button routerLink="/1">Go to 1</button>
    <button color='primary' mat-button routerLink="/2">Go to 2</button>
  </mat-toolbar>
</div>

Of course instead of two buttons you can also place your router-outlet inside the container with class theme1 and achive the desired result (different theme per lazy loaded module). Keep in mind that we are just importing import '[email protected]/material/theming';` in the places where we need custom theming.

Here is a live example: CodeSandbox

Related