有没有办法将 css 动态应用于我的子组件?



我有一个可重用的组件。此组件从父组件多次调用,有时父组件的背景页为白色,有时为黑色。

我的子组件动态生成表单标签 - 输入,选择,文本区域。

这意味着我不能在我的内容的组件的 css 中使用fixed样式。

因此,当背景页面为白色时 - 我的输入有一种样式 - 例如黑色背景。当背景页为黑色时,我的输入有另一种样式 - 例如白色 bacgrkound。

要解决这个问题是问题:

我试过了

在我的子组件 ts 文件中添加输入属性

@Input()
public cssTemplate;

在网页中

<div [ngClass]="{'form-group-white-bg': cssTemplate == 'white', 'form-group-dark-bg': cssTemplate == 'black'}">
<label for=""></label>
....

在 CHILD 组件中,我根据调用子组件的位置发送输入属性的值

如果在白色背景的页面上调用

<app-form-group cssTemplate="black" formControlName="address">
</app-form-group>

如果它被召唤在黑巴克格昆德

<app-form-group cssTemplate="white" formControlName="address" [data]="{ field: 'address', label: 'Address' }">
</app-form-group>

但这里的问题是,有时在我的父组件上,这个组件被称为乘法倍 在一个页面上可以调用 12 次,我需要 10 个输入和 2 个选择

在其他页面上可以调用15次等。

这意味着我需要重新审视自己 15 次

<app-form-group cssTemplate="white" formControlName="address">
</app-form-group>
<app-form-group cssTemplate="white" formControlName="someItherControlName">
</app-form-group>

到处都放cssTemplate="white".

ngFor不是选择,因为此子组件称为乘以,但不在父组件的 HTML 结构中的同一位置。

我该如何解决此DRY

根据父组件设置子组件的样式

在这种情况下,我通常采用两种方法

  1. :ng-deep - 基于父组件中设置的类创建样式规则
  2. 利用@ContentChildren()直接在子组件上设置属性,并在更改后手动调用detectChanges()

要采用第一种解决方案,您需要在 css 命名规则中更加小心,因为使用 ng-deep 显然会破坏这些样式规则的隔离。

采用第二种方法需要考虑,因为它在技术上绕过了 Angular 中的标准输入/输出流,因此对于应用程序的任何其他维护者来说,这可能是一个令人惊讶的"未记录行为"。

我有点犹豫我是否更喜欢一种方法而不是另一种方法。第一种方法对我来说似乎更微不足道,但它也可能导致意外的样式规则覆盖,而第二种方法涉及更多的脚本,似乎有点黑客。

方法1:ng深度

  1. 为您的父组件提供输入并更新包装<ng-content>的块元素上的类。
  2. 在子组件中创建所需的样式规则。
// parent component
@Component(...)
export class FooParent {
@Input() bgStyle: 'light' | 'dark' = 'light';
}
<!-- parent component template -->
<div class="parent" [ngClass]="{light: bgStyle == 'light', dark: bgStyle == 'dark'}">
<ng-content></ng-content>
</div>
// child.css
::ng-deep .light .child-container {
background-color: lightblue;
}
::ng-deep .dark .child-container {
background-color: royalblue;
}

我在示例中的目标元素是.child-container,您将为要影响的每个元素编写类似的样式规则。

方法 2:使用 ContentChild 传递值

  1. @ContentChildren()装饰器添加到父组件,该组件为子组件选择。
  2. 注入ChangeDetectorRef
  3. 实现 ngAfterViewInit 以遍历每个子项并设置值
  4. 完成后detectChanges()打电话。
  5. 像往常一样在子组件中添加ngClass指令。

父母

@Component({
selector: 'parent',
templateUrl: 'parent.component.html',
styleUrls: ['parent.component.scss']
})
export class ParentComponent implements AfterViewInit, OnChanges {
@Input() bgStyle: 'light' | 'dark' = 'light';
@ContentChildren(ChildComponent) childComponents!: QueryList<ChildComponent>;
constructor(private change: ChangeDetectorRef) {
}

ngOnChanges(changes: SimpleChanges) {
if ('bgStyle' in changes) {
this.updateChildComponents();
}
}
updateChildComponents() {
this.childComponents.forEach(child => {
child.bgStyle = this.bgStyle;
});
this.change.detectChanges();
}
ngAfterViewInit() {
this.updateChildComponents();
}
}
<!-- parent.component.html -->
<ng-content></ng-content>

孩子

@Component({
selector: 'child',
templateUrl: 'child.component.html',
styleUrls: ['child.component.scss']
})
export class ChildComponent implements OnInit {
bgStyle: 'light' | 'dark' = 'light';
constructor() {
}
ngOnInit(): void {
}
}
<!-- child.component.html -->
<div [ngClass]="{light: bgStyle == 'light', dark: bgStyle == 'dark'}" class="child-container"></div>
// child.component.css - you would apply styles as you needed obviously.
.child-container {
width: 40px;
height: 40px;
margin: .5rem;
}
.light.child-container {
background-color: lightblue;
}
.dark.child-container {
background-color: royalblue;
}

用法

<!-- any other template -->
<parent>
<child></child>
<child></child>
<child></child>
</parent>

注意:如果直接在ParentComponent自己的模板中创建ChildComponent,则需要使用@ViewChildren而不是@ContentChildren

您可以在样式中添加样式.css(所有应用程序的常规样式)。例如,如果您有

.white h1{
color:red;
}
.black h1{
color:green;
}

您可以在"父"中使用 [ngClass]

<div [ngClass]="toogle?'white':'black'">
<hello name="{{ name }}"></hello>
</div>
<button (click)="toogle=!toogle">toogle</button>

参见[堆栈闪电战][1]

注意:我使用的方式[ngClass]="expresion"(其中表达式使用条件运算符)比[ngClass]="{'classname1':condition;'classname2':!condition}"更好

更新您的评论"我如何防止在儿童电话中重复自我",真的我不太明白。我不知道你是否想做出这样的指令,例如

@Directive({
selector: 'hello', //<--the selector is the selector of the component
exportAs: 'helloDiv'
})
export class HelloDirective implements OnInit{
constructor(@Self() private component:HelloComponent,private dataService:DataService){
}
ngOnInit(){
console.log(this.component.name)
this.dataService.theme.subscribe(res=>{
this.component.theme=res;
})
}
}

这允许"扩展"组件 - 在堆栈闪电战中变量"主题"更改- [1]: https://stackblitz.com/edit/angular-ivy-sjwxyq?file=src%2Fapp%2Fapp.component.html

您可以使用输入属性创建要传递给 ngClass 的 css 类映射。此对象应该是字符串数组的对象。 它可以非常复杂,并且根据需要包含尽可能多的类和规则


@Input() color: 'white' | 'red' | 'hotpink' = 'white';
classMap: any;
ngOnInit() {
this.updateClassMap();
}
updateClassMap() {
this.classMap = {
[this.color]: !!this.color, // Add this class if not null
};
}

然后在 HTML 中简单地将其传递给 ngClass

<div [ngClass]="classMap">

最新更新