Как добавить "класс" для размещения элемента?

Я не знаю, как добавить в свой компонент <component></component> атрибут динамического класса, но внутри шаблона html (component.html).

Единственное решение, которое я нашел, - изменить элемент с помощью нативного элемента "ElementRef". Это решение кажется немного сложным, чтобы сделать что-то, что должно быть очень простым.

Другая проблема состоит в том, что CSS должен быть определен вне области компонента, нарушая инкапсуляцию компонента.

Есть ли более простое решение? Что-то вроде <root [class]="..."> .... </ root> внутри шаблона.

11 ответов

Решение
@Component({
   selector: 'body',
   template: 'app-element',
   // prefer decorators (see below)
   // host:     {'[class.someClass]':'someField'}
})
export class App implements OnInit {
  constructor(private cdRef:ChangeDetectorRef) {}

  someField: boolean = false;
  // alternatively also the host parameter in the @Component()` decorator can be used
  @HostBinding('class.someClass') someField: boolean = false;

  ngOnInit() {
    this.someField = true; // set class `someClass` on `<body>`
    //this.cdRef.detectChanges(); 
  }
}

Пример плунжера

Таким образом, вам не нужно добавлять CSS за пределы компонента. CSS как

:host(.someClass) {
  background-color: red;
}

работает изнутри компонента и селектор применяется только если класс someClass устанавливается на элементе хоста.

Методика, предложенная в этом ответе, устарела и больше не должна использоваться разработчиками Angular. Пожалуйста, смотрите предыдущий ответ для поддерживаемой опции.

Ответ Гюнтера великолепен (вопрос требует атрибута динамического класса), но я подумал, что добавлю только для полноты...

Если вы ищете быстрый и понятный способ добавления одного или нескольких статических классов к элементу host вашего компонента (т. Е. Для целей оформления тем), вы можете просто сделать:

@Component({
   selector: 'my-component',
   template: 'app-element',
   host: {'class': 'someClass1'}
})
export class App implements OnInit {
...
}

И если вы используете класс в теге entry, Angular объединит классы, т.е.

<my-component class="someClass2">
  I have both someClass1 & someClass2 applied to me
</my-component>

Вы можете просто добавить @HostBinding('class') class = 'someClass';внутри вашего класса @Component.

Пример:

@Component({
   selector: 'body',
   template: 'app-element'       
})
export class App implements OnInit {

  @HostBinding('class') class = 'someClass';

  constructor() {}      

  ngOnInit() {}
}

Если вы хотите добавить динамический класс к элементу хоста, вы можете объединить свои HostBinding с геттером как

@HostBinding('class') get class() {
    return aComponentVariable
}

Демо Stackblitz на https://stackblitz.com/edit/angular-dynamic-hostbinding

Другая проблема заключается в том, что CSS должен быть определен вне области действия компонента, что нарушает инкапсуляцию компонента.

Это неправда. С помощью scss (SASS) вы можете легко стилизовать компонент (сам; хост) следующим образом:

:host {
    display: block;
    position: absolute;
    width: 100%;
    height: 100%;
    pointer-events: none;
    visibility: hidden;

    &.someClass {
        visibility: visible;
    }
}

Таким образом, инкапсуляция не нарушается.

В дополнение к ответу @JoshuaDavid есть еще один способ определить статический класс, который работает на angular v8, когда я пытался (может также работать в более старых версиях):

      @Component({
selector: "my-component.someClass1.someClass2",
...
})

который будет генерировать следующий вывод:

      <my-component class="someClass1 someClass2">
 ...
</my-component>

вы также можете просто использовать этот способ:

      @Component({
selector: ".someClass1.someClass2",
...
})

который будет генерировать следующий вывод:

      <div class="someClass1 someClass2">
 ...
</div>

Для ситуации с несколькими классами, как упоминалось выше @jbojcic, вы можете использовать:

хост: {класс: 'A B C'}

вот что я сделал:

      import { Component, Attribute, HostBinding } from "@angular/core";

@Component({
    selector: "selector-el",
    template: ...                                            
})
export class MyComponent {
    @HostBinding('class') get classAttribute(): string {
        let defaultClasses = 'selector-el-class';
        return defaultClasses + ' ' + this.classNames;
    }

    constructor(
        @Attribute('class') public classNames: string
    ) { }
}

Не нашел ответов о подходе Renderer2 .

Вот что я использовал, например:

      constructor(
  private readonly elementRef: ElementRef,
  private readonly renderer: Renderer2
) { }


@Input()
set disabled(disabled: boolean) {
  if (disabled) {
    this.renderer.addClass(this.elementRef.nativeElement, 'disabled');
  }
}

или внутриngOnInit

      ngOnInit(): void {
    if (this.something.disabled) {
      this.renderer.addClass(this.elementRef.nativeElement, 'disabled');
    }
  }

Вот как я это сделал (Angular 7):

В компонент добавьте вход:

@Input() componentClass: string = '';

Затем в HTML-шаблон компонента добавьте что-то вроде:

<div [ngClass]="componentClass">...</div>

И, наконец, в шаблоне HTML, где вы создаете экземпляр компонента:

<root componentClass="someclass someotherclass">...</root>

Отказ от ответственности: я довольно новичок в Angular, так что, возможно, мне просто повезло здесь!

Когда у вас есть несколько классов, привязанных к хосту, я обнаружил, что следующее использованиеHostBindingгеттер наиболее удобен:

      @Component({ ... })
export class MyComponent {
    @Input()
    theme: 'success' | 'error';

    @HostBinding('class')
    get classes(): Record<string, boolean> {
        return {
            'my-component': true,
            'my-component-success-theme': this.theme == 'success',
            'my-component-error-theme': this.theme == 'error'
        }
    }
}

Даже для тривиальных привязок классов, которые не имеют соответствующей логики, это хороший способ управлять всеми вашими базовыми классами в одном месте.

      @Component({ ... })
export class MyComponent {
    @HostBinding('class')
    get classes(): Record<string, boolean> {
        return {
            'my-component': true,
            'my-component-modifier': true
        }
    }
}
Другие вопросы по тегам