如何将“class”添加到主机元素?

284

我不知道如何在组件的模板html(component.html)中添加动态class属性到<component></component>标签中。

我找到的唯一解决方案是通过“ElementRef”本机元素修改项目。但这个解决方案似乎有些复杂,做起来应该很简单。

另一个问题是CSS必须在组件范围之外定义,打破了组件封装性。

是否有更简单的解决方案?像<root [class]="..."> .... </ root>这样在模板中。

11个回答

444

这样您就不需要在组件外部添加CSS:

@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(); 
  }
}

Plunker示例

这个CSS是在组件内定义的,选择器只在主机元素(来自外部)上设置了类someClass时才应用:

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

这里有一个分支 Made a fork here,展示了实际的 :host 部分工作原理。我在哪里可以学习更多关于 @Component() 装饰器中 host 参数的知识(语法对我来说不是很明显,而且 @Component 文档并没有解释得很清楚),或者学习更多关于您首选的 HostBinding 的知识(它只被列为一个接口 listed as an Interface 在 Angular2 网站上?) - Nate Anderson
我不知道更好的文档,但这只是一种不同的方法,可以使用@Input() @Output() @HostBinding() @HostListener() @ViewChild(ren)() @ContentChild(ren)()所实现的功能。 - Günter Zöchbauer
@GünterZöchbauer 你会如何使用它,但是针对布尔值的相反情况。换句话说,当布尔值为false时,你如何在主机上设置类? - FreeAsInBeer
1
使用一个不同的名称为宿主绑定编写一个getter,该getter返回反转值@HostBinding('class.xxx') get xxxclass(){ return !this.someField;} - Günter Zöchbauer
1
@YochaiAkoka 不确定你指的是什么。我不知道这个规则。通常情况下,少即是多,所以如果你可以避免添加额外的元素,那么你应该避免它。 - Günter Zöchbauer
显示剩余6条评论

285

Günter的回答非常好(问题要求使用动态类属性),但为了完整起见,我想补充一下...

如果您正在寻找一种快速而简洁的方法来向组件的主机元素添加一个或多个静态类(即用于主题样式的目的),可以这样做:

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

如果你在入口标签上使用一个类,Angular将合并这些类,即

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

1
喜欢这种简单易用的方式。但在我的情况下,宿主元素与模板中的任何属性都不同,我们称之为 ngcontent_host,因此如果我在组件的 styleUrls 中放置样式,它们不会影响宿主元素,因为它们不会影响 ngcontent_host,它们只能影响模板元素;它们只能影响 ngcontent_template。我错了吗?对此有什么建议吗?我想我可以随时将 ViewEncapsulation.None 打开。 - Nate Anderson
16
另一种方法是跳过变量,@HostBinding('class.someClass') true;。你甚至可以在任何你的组件继承的类中这样做。 - adamdport
5
要添加多个类,可以这样做:host: { '[class]': '"class1 class2"' }。 - jbojcic
4
如果您使用主机:{}变量,您可能希望在tslint.json中将use-host-property-decorator设置为false。否则,您将收到IDE警告。@adamdport那种方法不起作用(不再起作用)。我们的应用程序中使用Angular 5.2.2。 - Ruud Voost
4
你的答案会创建一个名为"true"的变量,类型为"any"。不确定这是否是你想要的? - jonas
显示剩余8条评论

49

您可以在@Component类中简单地添加 @HostBinding('class') class = 'someClass';

例如:

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

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

  constructor() {}      

  ngOnInit() {}
}

6
className指令也可以使用,最好避免使用class作为变量名(因为您可能会引用它并稍后更改它)。例如:@HostBinding('className') myTheme = 'theme-dark'; - CPHPython
1
我肯定会避免将变量命名为“class”。一些包将其命名为“klass”或“cssClass”。 - Mick

21

11

无冗余信息(HOSTBINDING)纯CSS解决方案


另一个问题是CSS必须在组件范围之外定义,破坏了组件的封装性。

这不是真的。通过使用 scss (SASS) 你可以轻松地为组件本身(即host元素)添加样式,如下所示:

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

    &.someClass {
        visibility: visible;
    }
}

这样封装就是“完整的”。


1
由于其他答案有不同的设置组件类的方式,如果您使用此方式,您只需将您的类设置为组件上的类即可。因此,对于此答案中的示例,它应该是:<my-component class="someClass"></my-component>。 - mkimmet
1
我喜欢这个答案。正是我在寻找的。赞! - Morfinismo

3

对于多个类的情况,就像 @jbojcic 上面提到的那样,您可以使用:

host: {class: 'A B C'}


3

除了@JoshuaDavid的回答外,还有另一种定义静态类的方法,我在尝试时发现它适用于angularv8(可能也适用于旧版本):

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

以下是生成的输出结果:
<my-component class="someClass1 someClass2">
 ...
</my-component>

你也可以使用这种方式:
@Component({
selector: ".someClass1.someClass2",
...
})

这将生成以下输出:

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

1
如果我指定了 selector: "my-component.someClass1.someClass2",,那么我在 HTML 中使用的选择器就必须是 <my-component.someClass1.someClass2>... - Tony

1
当您拥有多个主机绑定类时,我发现以下使用 HostBinding getter 的方法最方便:
@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
        }
    }
}

你在get类函数中缺少了return语句。 - Tukkan

1

这是我所做的:

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
    ) { }
}

你的答案可以通过添加更多支持信息来改进。请编辑以添加进一步的细节,比如引用或文档,这样其他人就能确认你的答案是正确的。你可以在帮助中心找到更多关于如何撰写好答案的信息。 - Community

1

没有看到任何关于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');
    }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接