有什么方法可以将 css 动态应用于我的子组件?

时间:2021-04-22 06:15:18

标签: css angular

我有一个可重复使用的组件。这个组件被父组件多次调用,父组件的背景页面有时是白色的,有时是黑色的。

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

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

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

要解决这个问题:

我试过了

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

  @Input()
  public cssTemplate;

在 html 中

    <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>

如果它是在黑色 bacgrkound 上调用的

<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

3 个答案:

答案 0 :(得分:0)

您可以在styles.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>

见[stackblitz][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;
     })
  }
}

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

答案 1 :(得分:0)

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


    @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">

答案 2 :(得分:0)

根据父组件样式化子组件

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

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

要采用第一个解决方案,您需要更加注意 css 命名规则,因为使用 ng-deep 显然打破了这些样式规则的隔离。

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

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

方法一:ng-deep

  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:使用 ContentChildren 传递值

  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>

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

相关问题