Руководство по созданию тематического механизма, который воздействует на все компоненты в Angular?

Вопрос:

Мне нужно руководство по написанию механизма в Angular, чтобы установить "Look and Feel" компонентов во всем мире в моем приложении. Обратите внимание, что я пытаюсь изучить @ngrx/platform, и я подумал, что это будет интересное конструктивное ограничение; однако я готов отпустить его, если это просто не имеет смысла.

Структура:

У меня есть приложение, работающее со многими компонентами. Каждый компонент в моем приложении имеет в настоящее время 3 возможных "вида и ощущения (L & F)":

  • Утро (сепия)
  • Вторая половина дня (белый)
  • Вечер (темный)

Обратите внимание, что может быть спектр цветов, основанный на более гранулированном времени.

Эти L & Fs устанавливаются ко времени дня текущего пользователя, например, если текущее время пользователя равно 7:00, рассчитанный L & F будет установлен на "Morning". Я отслеживаю это состояние внутри ngrx/store модуля Angular под названием SundialModule и gnomon является механизмом редукторов и действий для состояния getting или setting:

солнечные часы/редукторы/gnomon.ts:

import * as gnomon from '../actions';

export interface State {
  currentHour: number,
}

const initialState: State = {
  currentHour: new Date().getHours()
};

export function reducer(state = initialState,
                        action: gnomon.Actions) {
  console.log(action, 'action');
    switch(action.type) {
      case gnomon.MORNING:
        return  {
          ...state,
          currentHour: 6,
        };
      case gnomon.AFTERNOON:
        return  {
          ...state,
          currentHour: 12,
        };
      case gnomon.EVENING:
        return  {
          ...state,
          currentHour: 7,
        };
      default:
        return state;
    }
}

Теперь у меня есть Angular директива атрибута под названием [sundialTheme], которая установит HostBinding('class') theme = 'light' для элемента, на который он был помещен.

/директивы Sundial/theme.ts

@Directive({
  selector: '[sundialTheme]'
})
export class SundialThemeDirective implements OnInit {
  @HostBinding('class') theme = 'light';

  private gnomon: Observable<any>;

  constructor(private store: Store<fromGnomon.State>) {
    this.gnomon = store.select<any>('gnomon');
  }

  ngOnInit() {
    this.gnomon.subscribe((theme) => {
      if(theme.currentHour >= 7 && theme.currentHour <= 11){
        this.theme = 'sepia';
      } else if( theme.currentHour >= 12 && theme.currentHour <= 18) {
        this.theme = 'light'
      } else {
        this.theme = 'dark'
      }
    });
  }
}

Проблема: Каждый компонент в моем приложении должен иметь этот атрибут sundialTheme; кроме того, каждый компонент будет иметь подписку на this.gnomon = store.select<any>('gnomon');, которая кажется дорогой/тяжелой. Наконец, и в качестве стороннего средства, это то, что каждому компоненту необходимо будет добавить мой SundialModule в каждый функциональный модуль, и каждому компоненту потребуется набор тем для каждого времени суток:

Пример этого, каждый компонент шаблона. note: sundialTheme директива атрибута:

<mh-pagination sundialTheme></mh-pagination>
<canvas glBootstrap class="full-bleed" sundialTheme></canvas>
<running-head [state]="(state$ | async)" sundialTheme></running-head>
<menu-navigation sundialTheme></menu-navigation>
<folio sundialTheme></folio>
<mh-footer sundialTheme></mh-footer>

Каждый функциональный модуль с зависимостью SundialModule:

@NgModule({
  imports: [
    SundialModule
  ],
})
export class MenuModule {
}

Каждый элемент styleUrls с sundial-links: обратите внимание на уродливые sundial-morning.scss

@Component({
  selector: 'running-head',
  templateUrl: 'running-head.component.html',
  styleUrls: [
    'running-head.component.scss',
    '../../../components/sundial/sundial-morning.scss', // This !
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RunningHeadComponent {
}

Наконец, у меня были другие способы, предлагаемые мне:

1) Так как я использую Angular CLI, я могу добавить свои стили во всем мире и иметь директиву, устанавливающий класс на теле. Что, похоже, нарушает любые стандарты веб-компонентов.

2) Я мог бы использовать загрузчик factory в каждом компоненте styleUrls:[]; которые я не понял, как реализовать.

3) Я мог бы следить за архитектурой компонентов материалов, которая добавляет темы непосредственно в компоненты, которые не чувствуют себя очень сухими? (однако, я слишком глубоко изучил)

4) Я мог бы сделать собственный декоратор для каждого компонента (может быть жизнеспособным, но я не уверен, как его реализовать)

Любые другие предложения? Любые лучшие практики с рассуждениями?

Ответ 1

@HostBinding('class') theme = 'light'

лучше

@HostBinding('class.light') isLight = theme === 'light';

чтобы не перезаписывать все остальные классы этого элемента.

Вы можете установить класс только в AppComponent и использовать в стилях компонентов

:host-context(.light) {
  // my light component styles here
}