Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2: Custom material theme in each lazy loaded submodule

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

like image 774
Leon Rokić Avatar asked Feb 11 '19 22:02

Leon Rokić


2 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
})
like image 70
Fmerco Avatar answered Nov 05 '22 14:11

Fmerco


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 config in the Material Guides)

import '~@angular/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 remember that you must include just once the mat-core() in your application (in order to have a single material styles css file).

After that you can create custtom themes by including import '~@angular/material/theming'; wherever 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 "~@angular/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 '~@angular/material/theming';` in the places where we need custom theming.

Here is a live example: CodeSandbox

like image 42
Християн Христов Avatar answered Nov 05 '22 14:11

Християн Христов