Ole
Ole

Reputation: 47090

Using Angular Signals with HostBinding to update style?

In a app-test component I have the following:

  @Input( { transform: booleanAttribute})
  reverse: boolean = false;

  @HostBinding('style.flex-direction')
  direction: string = this.reverse ? 'column-reverse' : 'column';

And so if the designer applies the reverse attribute to app-test like this:

<app-test reverse></app-test>

Then Angular should set style="flex-direction: column-reverse" on the app-test element.

And I'm wondering how to use Angular signals so that when reverse is set to true, direction will be set to column-reverse. Thoughts?

Upvotes: 16

Views: 8299

Answers (5)

grigson
grigson

Reputation: 3776

you can bind to host properties, attributes, classes, styles and events with host section of @Component decorator

host option is inherited from @Directive decorator, so you can use this technic also with directives.

host accepts binding dictionary with values being javascript expressions that can utilize signals

@Component({
  // ommited for brevity

  host: {
    // bind to classes
    'class': 'flex flex-col',
    '[class.active]': 'isActive()',   // <-- signal   

    // bind to some specific props
    '[tabIndex]': 'isDisabled() || isActive() ? -1 : 0',  // <-- also
    
    // bind to any attribute
    '[attr.aria-disabled]': 'isDisabled()',
    '[attr.aria-selected]': 'isActive()',

    // bind to style
    '[style.display]': 'isEnabled() ? "flex" : "none"',
    '[style.flex-direction]': 'isReversed() ? "column-reverse" : "column"',
    '[style.width]': 'progressSignal() + "px"'
    '[style.width.px]': 'progressSignal()'     // or directly to units
    '[style.width.%]': 'progressSignal() * 100'

    // bind css vars
    '[style.--component-color]': 'color()',

    // bind to events
    '(focus)': 'activateOption()', 
    '(click)': 'select($event)',

  }
})
export class SomeComponent {
  // ommited
}

Upvotes: 21

Jandro Rojas
Jandro Rojas

Reputation: 1972

I would use an effect for that.

All these examples use a getter in the hostBinding but the thing with getters is that they're actually functions that will be executed every time change detection needs to "check" if the value has changed.

I would just do:

@Component({...})
public class MyComponent {
   public reverse = input<boolean>(false);
   @HostBinding('style.flex-direction') public reversedStyling = false;

   constructor(){
     effect(() => this.reversedStyling = this.reverse());
   } 
}

I'm sure the angular team will introduce a signal-based version of HostBinding and HostListeners sooner rather than later though.

Upvotes: 8

Ole
Ole

Reputation: 47090

My current project was on Angular 16, so this is what I ended up with (Since input is for Angular 17+):

  @Input( { transform: booleanAttribute})
  reverse:boolean = false;

  @HostBinding('style.flex-direction')
  get direction() { return this.reverse ? 'column-reverse' : 'column' }

Upvotes: 0

Matthieu Riegler
Matthieu Riegler

Reputation: 55669

This is being discussed in this issue. Currently the combination of @HostBinding and signals is not supported.

The workaround for this is to use a getter:

reverse = input.required<boolean>();

@HostBinding('attr.style.flex-direction')
get direction() { return this.reverse() ? 'column-reverse' : 'column' }

Upvotes: 11

Benny Halperin
Benny Halperin

Reputation: 2332

You can do the following:

@Component({
  selector: 'app-child',
  standalone: true,
  template: 'My name (red is {{ red() }})',
})
export class AppChildComponent {
  red = input(false);

  @HostBinding('style.color')
  get color() {
    return this.red() ? 'red' : 'green';
  }
}

And in the container component:

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [AppChildComponent],
  template: `
    <app-child [red]="true"></app-child>
  `,
})
export class App {
}

Working example here

Upvotes: 1

Related Questions