angulartypescriptangular-directive

Angular hostDirectives API Not Passing @Input Values Correctly in Composed Directives


I'm encountering an issue with Angular's hostDirectives API, specifically when trying to pass values through @Input in composed directives.

I have a standalone directive PgcTypoDirective that applies a CSS class based on a @Input value:

import { Directive, HostBinding, Input } from '@angular/core';

@Directive({
  selector: '[pgc-typo]',
  standalone: true,
})
export class PgcTypoDirective {
  @Input('pgc-typo')
  typography = '';

  @HostBinding('class')
  get className() {
    return `pgc-typo--${this.typography}`;
  }
}

Then, I created another directive Heading1Directive that composes PgcTypoDirective using the hostDirectives API and attempts to pass a value to the pgc-typo input:

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

@Directive({
  selector: '[heading-1]',
  standalone: true,
  hostDirectives: [
    {
      directive: PgcTypoDirective,
      inputs: ['pgc-typo: heading-1'],
    },
  ],
})
export class Heading1Directive {}

Finally, I use Heading1Directive in a component:

<h1 heading-1>
  This is a Heading 1 styled text.
</h1>

Expected Behavior

I expect the PgcTypoDirective to receive the value 'heading-1' and apply the class 'pgc-typo--heading-1' to the element.

Actual Behavior

Instead of applying the correct class, I see that the resulting class is just 'pgc-typo--', which suggests that the value 'heading-1' was not correctly passed to PgcTypoDirective.

Is this expected behavior for Angular's hostDirectives API? Shouldn't the inputs array in hostDirectives correctly map and pass these values from Heading1Directive to PgcTypoDirective? Or is there a different recommended approach to ensure the correct @Input value is passed when using directive composition?

EDIT: WORKAROUND

@Directive({
  selector: '[heading-1]',
  standalone: true,
  hostDirectives: [PgcTypoDirective],
})
export class RtlsHeading1Directive {
  readonly #pgcTypoDirective = inject(PgcTypoDirective);

  constructor() {
    this.#pgcTypoDirective.typography = 'heading-1';
  }
}

This workaround helped me to achieve the desired outcome. 'pgc-typo--heading-1' class is applied.

<h1 heading-1>
  This is a Heading 1 styled text.
</h1>

Solution

  • You are not providing any value to pgc-typo.

    According to the documentation and sample, "pgc-typo: heading-1" in your inputs with "heading-1" is an alias/variable that passes the value to the pgc-typo input variable, but not passing the "heading-1" text as value.

    With alias: Make sure that you are giving value to the heading-1 attribute as below:

    <h1 heading-1="heading-1">
      This is a Heading 1 styled text.
    </h1>
    

    Or

    Without alias: You should remove the alias to avoid confusion.

    heading-1.directive.ts

    @Directive({
      selector: '[heading-1]',
      standalone: true,
      hostDirectives: [
        {
          directive: PgcTypoDirective,
          inputs: ['pgc-typo'],
        },
      ],
    })
    export class Heading1Directive {}
    
    <h1 pgc-typo="heading-1">
     This is a Heading 1 styled text.
    </h1>
    

    Demo @ StackBlitz