Angular 2 - Apply conditional style to a directive's child HTML element
Asked Answered
R

3

7

I am trying to apply a class to an HTML element based on a click event. This works fine when setting the class property for the child component's selector from the parent component's template as seen by the following snippet from the parent component:

[class.bordered]='isSelected(item)'

This will appropriately set the style when that item is clicked. However, I want to set an inner HTML element's class in the child component based on the same sort of click event, here's the desired target for the style for the child component:

template: `
  <div class="This is where I really want to apply the style">  
    {{ item.val }}
  </div>
`

Is there a way to do this that is easily supported? Or is this considered a bad practice and I should design my components to avoid this sort of conditional styling situation?

Full code:

@Component({
  selector: 'parent-component',
  directives: [ChildComponent],
  template: `
    <child-component
      *ngFor='#item of items'
      [item]='item'
      (click)='clicked(item)'
      [class.bordered]='isSelected(item)'>
    </child-component>
  `
})
export class ParentComponent {
  items: Item[];
  currentItem: item;

  constructor(private i: ItemService) {
    this.items = i.items;
  }

  clicked(item: Item): void {
    this.currentItem = item;
  }

  isSelected(item: Items): boolean {
    if (!item || !this.currentItem) {
      return false;
    }
    return item.val === this.currentItem.val;
  }
}


@Component({
  selector: 'child-component',
  inputs: ['item'],
  template: `
    <div class="This is where I really want to apply the style">  
      {{ item.val }}
    </div>
  `
})
export class ChildComponent {}
Ratepayer answered 24/1, 2016 at 17:53 Comment(0)
R
4

I found a better way to solve this making good use of Angular2 features.

Specifically instead of doing trickery with :host and CSS functionality you can just pass in a variable to the child component by changing:

[class.bordered]='isSelected(item)'

being set in the element of the child class change it to

[isBordered]='isSelected(item)'

Then on the div you'd like to apply the bordered class to in the template of the child component just add:

[ngClass]='{bordered: isBordered}'

Here is the full code with the change:

@Component({
  selector: 'parent-component',
  directives: [ChildComponent],
  template: `
    <child-component
      *ngFor='#item of items'
      [item]='item'
      (click)='clicked(item)'
      [isBordered]='isSelected(item)'>
    </child-component>
  `
})
export class ParentComponent {
  items: Item[];
  currentItem: item;

  constructor(private i: ItemService) {
    this.items = i.items;
  }

  clicked(item: Item): void {
    this.currentItem = item;
  }

  isSelected(item: Items): boolean {
    if (!item || !this.currentItem) {
      return false;
    }
    return item.val === this.currentItem.val;
  }
}


@Component({
  selector: 'child-component',
  inputs: ['item'],
  template: `
    <div [ngClass]='{bordered: isBordered}'>
      {{ item.val }}
    </div>
  `
})
export class ChildComponent {}
Ratepayer answered 30/1, 2016 at 0:32 Comment(0)
G
4

Add a style to the child-component

@Component({
  selector: 'child-component',
  inputs: ['item'],
  template: `
    <div class="This is where I really want to apply the style">  
      {{ item.val }}
    </div>
  `,
  styles: [`
    :host(.bordered) > div {
    // if this selector doesn't work use instead
    // child-component.bordered > div {
      border: 3px solid red;
    }
  `],
})
export class ChildComponent {}
Glorify answered 24/1, 2016 at 17:57 Comment(1)
While this did solve the problem, I prefer the cleaner solution I found below.Ratepayer
R
4

I found a better way to solve this making good use of Angular2 features.

Specifically instead of doing trickery with :host and CSS functionality you can just pass in a variable to the child component by changing:

[class.bordered]='isSelected(item)'

being set in the element of the child class change it to

[isBordered]='isSelected(item)'

Then on the div you'd like to apply the bordered class to in the template of the child component just add:

[ngClass]='{bordered: isBordered}'

Here is the full code with the change:

@Component({
  selector: 'parent-component',
  directives: [ChildComponent],
  template: `
    <child-component
      *ngFor='#item of items'
      [item]='item'
      (click)='clicked(item)'
      [isBordered]='isSelected(item)'>
    </child-component>
  `
})
export class ParentComponent {
  items: Item[];
  currentItem: item;

  constructor(private i: ItemService) {
    this.items = i.items;
  }

  clicked(item: Item): void {
    this.currentItem = item;
  }

  isSelected(item: Items): boolean {
    if (!item || !this.currentItem) {
      return false;
    }
    return item.val === this.currentItem.val;
  }
}


@Component({
  selector: 'child-component',
  inputs: ['item'],
  template: `
    <div [ngClass]='{bordered: isBordered}'>
      {{ item.val }}
    </div>
  `
})
export class ChildComponent {}
Ratepayer answered 30/1, 2016 at 0:32 Comment(0)
D
0

Something like this works very nicely for me:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  styleUrls: [ './app.component.css' ],
  template: `
  <button 
    (click)='buttonClick1()' 
    [disabled] = "btnDisabled"
    [ngStyle]="{'color': (btnDisabled)? 'gray': 'black'}">
    {{btnText}}
  </button>`
})
export class AppComponent  {
  name = 'Angular';
  btnText = 'Click me';
  btnDisabled = false;
  buttonClick1() {
    this.btnDisabled = true;
    this.btnText = 'you clicked me';
    setTimeout(() => {
      this.btnText = 'click me again';
      this.btnDisabled = false
      }, 5000);
  }
}

Here's a working example:
https://stackblitz.com/edit/example-conditional-disable-button?file=src%2Fapp%2Fapp.component.html

Damnable answered 3/1, 2019 at 0:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.