it-swarm.com.de

Angular 2 Material Dynamische Themen

Ich habe mein eigenes Scss-Theme erstellt und es in angle-cli.json deklariert, alles funktioniert gut.

Jetzt muss ich das Thema dynamisch ändern. 

Ich habe versucht, das zweite Thema in angle-cli.json einzufügen, überschreibt jedoch wie erwartet das erste. 

Möglicherweise besteht eine Option darin, die Themendeklaration von angle-cli.json zu entfernen und zwei Komponenten mit jeweils einem eigenen scss-Stil zu haben, wobei einer den anderen überschreibt. Der einzige Unterschied besteht in den styleUrls.

Oder gibt es eine andere empfohlene Methode, um einen SCSS dynamisch zu laden? 

13
Monica L

Seit Angular 5.1 habe ich so dynamische Themenwechsel erreicht.

* Bearbeiten: Dies funktioniert immer noch ab Angular 7+

Beispiel für die Bearbeitung eines Editors - https://stackblitz.com/edit/dynamic-material-theming

In meine theme.scss-Datei füge ich ein Standarddesign hinzu (beachten Sie, dass es nicht unter einem Klassennamen aufbewahrt wird - dies ist also der Fall, das Angular als Standard verwendet) und dann ein helles und dunkles Design.

theme.scss

@import '[email protected]/material/theming';
@include mat-core();

// Typography
$custom-typography: mat-typography-config(
  $font-family: Raleway,
  $headline: mat-typography-level(24px, 48px, 400),
  $body-1: mat-typography-level(16px, 24px, 400)
);
@include angular-material-typography($custom-typography);

// Default colors
$my-app-primary: mat-palette($mat-teal, 700, 100, 800);
$my-app-accent:  mat-palette($mat-teal, 700, 100, 800);

$my-app-theme: mat-light-theme($my-app-primary, $my-app-accent);
@include angular-material-theme($my-app-theme);

// Dark theme
$dark-primary: mat-palette($mat-blue-grey);
$dark-accent:  mat-palette($mat-amber, A200, A100, A400);
$dark-warn:    mat-palette($mat-deep-orange);

$dark-theme:   mat-dark-theme($dark-primary, $dark-accent, $dark-warn);

.dark-theme {
  @include angular-material-theme($dark-theme);
}

// Light theme
$light-primary: mat-palette($mat-grey, 200, 500, 300);
$light-accent: mat-palette($mat-brown, 200);
$light-warn: mat-palette($mat-deep-orange, 200);

$light-theme: mat-light-theme($light-primary, $light-accent, $light-warn);

.light-theme {
  @include angular-material-theme($light-theme)
}

In der app.component-Datei füge ich OverlayContainer von @ angle/cdk/overlay ein. Hier finden Sie die Dokumentation von Angular https://material.angular.io/guide/theming ; obwohl ihre Implementierung ein wenig anders ist. Bitte beachten Sie, dass ich OverlayModule auch als Import in app.module verwenden musste.

In meiner app.component-Datei habe ich auch @HostBinding('class') componentCssClass; als Variable deklariert, die verwendet wird, um das Design als Klasse festzulegen.

app.component.ts

import {Component, HostBinding, OnInit} from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Version } from './classes/version';
import { OverlayContainer} from '@angular/cdk/overlay';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {

  constructor(private http: HttpClient, public overlayContainer: OverlayContainer) {}

  title = 'app';
  version: Version;
  @HostBinding('class') componentCssClass;

  ngOnInit() {
    this.getVersion();
  }

  onSetTheme(theme) {
    this.overlayContainer.getContainerElement().classList.add(theme);
    this.componentCssClass = theme;
  }

  getVersion() {
    this.http.get<Version>('/api/version')
      .subscribe(data => {
        this.version = data;
      });
  }

}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { HttpClientModule } from '@angular/common/http';

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';

import { AppComponent } from './app.component';

import { OverlayModule} from '@angular/cdk/overlay';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    BrowserAnimationsModule,
    MatCardModule,
    MatButtonModule,
    OverlayModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

Rufen Sie schließlich die Funktion onSetTheme aus Ihrer Ansicht auf.

app.component.html

<button mat-raised-button color="primary" (click)="onSetTheme('default-theme')">Default</button>
<button mat-raised-button color="primary" (click)="onSetTheme('dark-theme')">Dark</button>
<button mat-raised-button color="primary" (click)="onSetTheme('light-theme')">Light</button>

Sie könnten ein Observable verwenden, um die Funktionalität dynamischer zu gestalten.

21
K. Waite

Ich habe meine Antwort in Change Material für Angular 2 gefunden. Es gibt ein gutes GIT-Beispiel unter https://github.com/jelbourn/material2-app .

Also verwende ich dieselbe Scss-Theme-Datei, fügte jedoch eine neue Klasse für das neue Theme hinzu:

.m2app-dark {
  $dark-primary: md-palette($md-pink, 700, 500, 900);
  $dark-accent:  md-palette($md-blue-grey, A200, A100, A400);
  $dark-warn:    md-palette($md-deep-orange);
  $dark-theme: md-dark-theme($dark-primary, $dark-accent, $dark-warn);
  @include angular-material-theme($dark-theme);
}

Diese wird in der HTML verwendet und ist abhängig vom Wert eines Booleschen Objekts aktiv oder nicht:

 <md-sidenav-layout [class.m2app-dark]="isDarkTheme">
5
Monica L

AKTUALISIEREN:

Eine neue Version dieser Lösung wurde hier veröffentlicht:

https://github.com/mirismaili/angular-material-dynamic-themes

Video


ARCHIVIERTE ANTWORT:

Vielen Dank an K.Waite. Ich habe seine/ihre Antwort verwendet. Ich habe versucht, es zu verbessern. Die wichtigste Änderung ist die Verwendung von .replace() anstelle von .add() für classList (in setTheme() Methode). Es gibt auch einige andere Funktionen, die Sie unten sehen können:


enter image description here

Stackblitz hier


Wichtigste Teile:

In Ihrem styles.scss (oder themes.scss Wenn Sie haben):

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

@include mat-core();

@mixin define-css-classes($theme) {
    @include angular-material-theme($theme);

    $primary: map-get($theme, primary);
    $accent: map-get($theme, accent);
    $warn: map-get($theme, warn);
    $background: map-get($theme, background);
    $foreground: map-get($theme, foreground);

    // CSS THEME-DEPENDENT-STYLES ARE HERE:
    .theme-dependent-colors {
        background: mat-color($primary);
        color: mat-color($accent);
    }
}

/**
* Define your custom themes in this map. 
* The `key` of each member is the name of CSS class for that theme. 
* To better understand the schema of the map, see `@each` loop below and especially pay attention to `map-has-key()` functions.
*/ 
$app-themes: (
        Indigo-pink : (primary-base: $mat-Indigo, accent-base: $mat-pink),
        deeppurple-amber: (primary-base: $mat-deep-purple, accent-base: $mat-amber),
        pink-bluegrey : (primary-base: $mat-pink, accent-base: $mat-blue-gray, is-dark: true),
        purple-green : (primary-base: $mat-purple, accent-base: $mat-green, is-dark: true),
);

@each $css-class, $theme in $app-themes {
    $primary: if(map-has-key($theme, primary), map-get($theme, primary), mat-palette(map-get($theme, primary-base)));

    $accent: if(map-has-key($theme, accent), map-get($theme, accent), mat-palette(map-get($theme, accent-base)));

    $warn: if(map-has-key($theme, warn), map-get($theme, warn), mat-palette(
            if(map-has-key($theme, warn-base), map-get($theme, warn-base), $mat-red)
    ));

    .#{$css-class} {
        @include define-css-classes(mat-light-theme($primary, $accent, $warn));
    }

    .#{$css-class}-dark {
        @include define-css-classes(mat-dark-theme($primary, $accent, $warn));
    }

    .theme-primary.#{$css-class} {
        background-color: mat-color($primary);
    }

    ...
}

In TypeScript (siehe hier ):

import {Component, HostBinding} from '@angular/core';
import {OverlayContainer} from "@angular/cdk/overlay";

const THEME_DARKNESS_SUFFIX = `-dark`;

export class AppComponent {
    @HostBinding('class') activeThemeCssClass: string;
    isThemeDark = false;
    activeTheme: string;

    setTheme(theme: string, darkness: boolean = null) {
        if (darkness === null)
            darkness = this.isThemeDark;
        else if (this.isThemeDark === darkness) {
            if (this.activeTheme === theme) return;
        } else
            this.isThemeDark = darkness;

        this.activeTheme = theme;

        const cssClass = darkness === true ? theme + THEME_DARKNESS_SUFFIX : theme;

        const classList = this.overlayContainer.getContainerElement().classList;
        if (classList.contains(this.activeThemeCssClass))
            classList.replace(this.activeThemeCssClass, cssClass);
        else
            classList.add(cssClass);

        this.activeThemeCssClass = cssClass;
    }

    constructor(overlayContainer: OverlayContainer) {
        this.setThemeClass('Indigo-pink', false); // Default theme
    }
}

Siehe andere Dinge in Stackblitz .


CAVEAT: Durch Hinzufügen von 8 dynamischen Materialthemen zur App (4 Lichter + 4 Dunkel) wurde die Größe des erstellten styles.css durch ~420 kB in meinem Fall (verglichen mit einem statischen Materialthema).

2
Mir-Ismaili

Sie können zwischen Designs wechseln, indem Sie zur Laufzeit basierend auf dem aktuellen Design die CSS-Klasse (body-Tag) hinzufügen oder entfernen.

zum Beispiel Schritt 1.

fügen Sie eine ID zum body-Tag in der HTML-Datei hinzu, sodass Sie Elemente für Elemente ablegen können.

<body id="themeTag">
<app-root></app-root>
</body>

Schritt 2.

erstellen Sie ein zweites Design in Ihrer Scss-Datei, die in Winkelcode 6 in angular.json und in Winkelversion unter 6 unter .angular-cli.json enthalten ist.

@include mat-core();
$primary: mat-palette($mat-blue);
$accent: mat-palette($mat-yellow);
$warn: mat-palette($mat-red);

$light-theme: mat-light-theme($primary, $accent, $warn);

@include angular-material-theme($light-theme);

$dark-theme: mat-dark-theme($primary, $accent, $warn);

.dark-theme { // css class for dark theme
  @include angular-material-theme($dark-theme);
}

Schritt 3.

klicken Sie auf die Schaltfläche zum Ändern der Klasse des Körper-Tags

toggleTheme(){
   this.isDarkTheme = !this.isDarkTheme;
   if(this.isDarkTheme){
     /* here themeTag is id of body tag and dark-theme is css class created in theme file */
     document.getElementById('themeTag').classList.add('dark-theme');
   }else{
     document.getElementById('themeTag').classList.remove('dark-theme');
   }
}
1
Ravi Sevta

@K Waite (nicht genügend Mitarbeiter, um einen Kommentar abzugeben.)

Ab Angular 8 (möglicherweise früher?)

Ich fand es notwendig, die zusätzliche Zeile classList.remove(this.componentCssClass); hinzuzufügen. Beim Testen habe ich festgestellt, dass beim Anwenden dynamischer Designänderungen auf Modal-Popup-Fenster das Modal-Fenster nur das letzte Design in der Klassenliste anwendet. Ein Wechsel von dunklen zu hellen Themen kann daher zu fehlerhaften Zuständen führen.

Erzwingen einer Klassenliste mit zwei Elementen (das default-theme und aktuell aktives Theme wird aufgelöst)

onSetTheme(theme) {      

  this.overlayContainer.getContainerElement().classList.remove(this.componentCssClass);
  this.overlayContainer.getContainerElement().classList.add(theme);
  this.componentCssClass = theme;
}
0
Corky3892