Динамическое изменение styles.scss в Angular

Я хочу применять динамически SCSS-стиль в соответствии с переменной, известной до начальной загрузки. Можно styles.scss программным образом изменить styles.scss? В main.ts перед начальной main.ts я хочу добавить динамически строку типа:

@import 'assets/styles/customerA/_themes.scss';
                       or 
@import 'assets/styles/customerB/_themes.scss';

в зависимости от значения переменной, поступающего с сервера.

Если вышеуказанное не представляется возможным, есть ли способ заполнить styles.scss содержимым другого файла, который будет определен на сервере? Я хочу изменить styles.scss а не любой другой "локальный" scss файл.

Ответ 1

После того, как я много боролся с этим, я нашел ответ в CSS variables. Это именно то, что я искал! Вы можете изменить их во время выполнения, и поэтому динамическая тематика выполнима. Это очень хорошая статья, описывающая шаги в Angular:

https://medium.com/@amcdnl/theming-angular-with-css-variables-3c78a5b20b24

Ответ 2

Я бы порекомендовал вам следующую общую форму сайта Angular Material: https://github.com/angular/material.angular.io/blob/master/angular.json. Стоит клонировать это репо и играть с ним локально. Есть несколько движущихся частей.

В вашем файле angular.json вы добавите css следующим образом. Обратите внимание, что вам нужно будет скомпилировать ваш scss для css отдельно, чтобы это работало. Они делают это в скрипте tools/build-theme.sh. :

{
  "input": "src/assets/customer-1-theme.css",
  "lazy": true,
  "bundleName": "customer-1-theme"
},
{
  "input": "src/assets/customer-2-theme.css",
  "lazy": true,
  "bundleName": "customer-2-theme"
},

Затем посмотрите на менеджера стиля: https://github.com/angular/material.angular.io/blob/master/src/app/shared/style-manager/style-manager.ts

Вы создадите функцию для динамического добавления и удаления этой таблицы стилей по мере необходимости:

function createLinkElementWithKey(key: string) {
  const linkEl = document.createElement('link');
  linkEl.setAttribute('rel', 'stylesheet');
  linkEl.classList.add(getClassNameForKey(key));
  document.head.appendChild(linkEl);
  return linkEl;
}

Затем в ThemePicker вы создадите наблюдаемый, чтобы управлять настройкой и удалением темы. См. (Https://github.com/angular/material.angular.io/blob/master/src/app/shared/theme-picker/theme-picker.ts):

installTheme(themeName: string) {
  const theme = this.themes.find(currentTheme => currentTheme.name === themeName);
  if (!theme) {
    return;
  }
  this.currentTheme = theme;
  if (theme.isDefault) {
    this.styleManager.removeStyle('theme');
  } else {
    this.styleManager.setStyle('theme', 'assets/${theme.name}.css');
  }

  if (this.currentTheme) {
    this._themeStorage.storeTheme(this.currentTheme);
  }
}

Лично я об этом поступил несколько иначе, но это означало, что scss компилируется в один файл css, и класс выбирается динамически. Их способ добавляет дополнительный шаг сборки, но позволяет скомпилировать scss для ленивой загрузки.

Управление глобальными стилями

Вы заметите в своем файле angular.json, что у них есть main.scss, который используется независимо от выбранного вторичного стиля. Этот файл будет обрабатывать любые глобальные темы, которые вам нужны. Обратите внимание, что в их main.scss они импортируют несколько партиций Sass partials. Они позволяют устанавливать глобальные стили для любой темы. Вы можете исследовать, как они используются, основная причина, по которой они используют частичные, заключается в том, что они могут передавать переменные из вторичных sass файлов вплоть до этих частичных данных с помощью mixins.

@mixin material-docs-app-theme($theme) {
  $primary: map-get($theme, primary);
  .docs-primary-header {
    background: mat-color($primary);

map-get - это функция Sass, которая позволяет вам определять объект и использовать его значения в Mixins. Конструкция материалов имеет функцию sass, которую они используют здесь, но вы можете использовать более простой способ захватить собственный цвет в вашем main.scss. В вашем стиле-a/style-b scss создайте настраиваемую тему.

$my-custom-theme: (
  background-color: #ffff,
  button-background: rgba(200, 200, 200, 0.6),
  foreground: #34383c
);

Затем вы измените свой @include на:

@include material-docs-app-theme($theme, $my-custom-theme);

Материал-docs-app-theme mixin изменится на:

@mixin material-docs-app-theme($theme, $my-custom-theme) {
  $background-color: map-get($my-custom-theme, background-color);
  .our-background {
    background-color: $background-color;
  }

Это позволяет вам определять пользовательские цвета в отдельных листах с ленивым загружением и иметь их в глобальном main.scss. Mixins и Partials от Sass делают это возможным.

Ответ 3

  ngOnInit() {
    // Do some stuff to figure out which css url you want
    this.cssUrl = '/assets/styles1.css';
  }

В вашем html файле

<link rel="stylesheet" [href]='sanitizer.bypassSecurityTrustResourceUrl(cssUrl)'>

Ответ 4

У меня есть 2 идеи, с которыми вы могли бы поиграть...

1) Угловые компоненты имеют стили, которые могут принимать файлы.scss. Однако, похоже, вы не можете легко заменить свойство stylesUrl. Однако вы можете легко заменить сам компонент...

В основном ваше фактическое приложение будет обернуто в приложение "shell", и только ответственность за то, чтобы он втянул правильный файл.scss. Более конкретно, это было бы обернуто в две разные оболочки, по одному для каждого стиля.

@Component({
  selector: 'style-one-page',
  templateUrl: './app.component.html',
  styleUrls: ['./app-style-one-page.scss']
})
export class WrapperStyleOneComponent extends PageComponent {}

@Component({
  selector: 'style-two-page',
  templateUrl: './app.component.html',
  styleUrls: ['./app-style-two-page.scss']
})
export class WrapperStyleTwoComponent extends PageComponent {}

Тогда тривиально заменить 2 компонента.

2) Моей второй идеей было бы использовать The Angular Material Themes, их можно легко поменять как таковые:

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

@function custom-light-theme() {
  @return (
    primary: $primary-palette,
    accent: $accent-palette,
    warn: $warn-palette,
    foreground: $foreground-palette,
    background: $background-palette,
    is-dark: false
  );
}

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

.dark-theme {
  $mat-dark-theme: mat-dark-theme($dark-primary-palette, $dark-accent-palette, $dark-warn-palette);
  @include angular-material-theme($mat-dark-theme);
}

Ответ 5

Просто идея, которая может работать. (происхождение)

Предполагается, что для изменения файла среды, но я предполагаю, что он должен работать и для стилей.

Вы можете изменить файл стилей с помощью fileReplacements в angular.json, но это потребует, чтобы вы каждый раз перестраивали приложение.

В вашем файле angular.json:

проекты> public> architect> build> конфигурации

"customerA": {
  "fileReplacements": [
    {
      "replace": "src/styles.scss",
      "with": "assets/styles/customerA/_themes.scss"
    }
  ]
},
"customerB": {
  "fileReplacements": [
    {
      "replace": "src/styles.scss",
      "with": "assets/styles/customerB/_themes.scss"
    }
  ]
}

проекты> public> architect> serve> конфигурации

"customerA": {
  "browserTarget": "public:build:customerA"
},
"customerB": {
  "browserTarget": "public:build:customerB"
},

Теперь вы можете запустить ng serve -c customerA или ng serve -c customerB чтобы получить соответствующую таблицу стилей.

Когда вы будете готовы к производству, вы можете либо развернуть вручную дважды, либо попробовать эту идею для перевода углового приложения.

PS Если вам нужно изменить только несколько стилей, я могу предположить, что вы используете переменные css.