Directive Composition API

What is Directive?

  • Subset of Component
  • Does not contain template and CSS
  • Consist of Behaviour
  • Can easily attached to host element
<my-elemnent drag hightlight>
</my-elemnent>

Pizza 😋

Blink Directive

@pankajparkar

www.pankajparkar.com

@Directive({
  selector: '[appBlink]',
  standalone: true,
})
export class BlinkDirective {
  @HostBinding('style.animation')
  animation = 'blinker 1s step-start infinite';

  @Input() theme = 'red';
  @Output() elementClick = new EventEmitter<MouseEvent>();

  @HostListener('click', ['$event'])
  click(event: MouseEvent) {
    this.elementClick.emit(event);
  }
}

Bold Directive

@pankajparkar

www.pankajparkar.com

@Directive({
  selector: '[appBold]',
  standalone: true
})
export class BoldDirective {
  @HostBinding('style.font-weight')
  bold = 'bolder';
}

My Text

My Text

Ribbon Directive

@pankajparkar

www.pankajparkar.com

@Component({
  selector: 'app-ribbon',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="ribbon"
      [ngClass]="placement ?? 'ribbon-top-left'">
      <span>sale</span>
    </div>
  `,
  styles: [`...`]
})
export class RibbonComponent {
  @Input() placement: string | null | undefined 
    = 'ribbon-top-right';
}

How to use this directive together

@pankajparkar

www.pankajparkar.com

Applying directly on component

 <app-ribbon appBold appBlink>
 </app-ribbon>

How to use this directive together ctd.

@pankajparkar

www.pankajparkar.com

@Component({
  selector: 'app-ribbon',
  standalone: true,
  ...,
})
export class RibbonComponent extends BoldDirective {
  ...
}

Apply single directive

@pankajparkar

www.pankajparkar.com

@Component({
  selector: 'app-ribbon',
  standalone: true,
  ...,
})
export class RibbonComponent extends WithBoldAndBlink {
  ...
}
class WithBold extends BoldDirective {
}
class WithBoldAndBlink extends BlinkDirective {
}

How to use this directive together ctd.

Apply multiple directive

Pankaj P. Parkar

Principal Application Devloper

  • Ex- Microsoft MVP (2015-22)

  • Angular GDE

About Me!

pankajparkar

#devwhorun 🏃‍♂️ - Feb 100km approx

#ngIndia2023

  • Speakers from 11 countries
  • 15 Talks
  • 600 attendees

What is Directive Composition API?

  • Simplified way attach directives together
  • Use `hostDirectives` property inside directive/component
@Component({
  selector: 'my-component',
  standalone: true,
  template: `<p>My Component</p>`
  hostDirectives: [MyDirective],
  imports: [],
})
export class MyComponent { }

Directive Composition API

@Component({
  selector: 'app-ribbon',
  standalone: true,
  imports: [],
  hostDirectives: [
    BlinkDirective
    BoldDirective,
  ],
  template: `
    <div class="ribbon"
      [ngClass]="placement ?? 'ribbon-top-left'">
      <span>sale</span>
    </div>
  `,
  styles: []
})
export class RibbonComponent {... }
<app-ribbon />

Pass Inputs and Outputs

@Component({
  selector: 'app-ribbon',
  standalone: true,
  imports: [],
  hostDirectives: [
    {
      directive: BlinkDirective,
      inputs: ['theme: bannerTheme'],
      outputs: ['elementClick: bannerClick'],
    },
    BoldDirective,
  ],
  template: `...`,
  styles: []
})
export class RibbonComponent { }
<app-ribbon
  bannerTheme="'black'"
  (bannerClick)="bannerClick($event)" />

What are the Real World

Use-cases? 🤔🤔🤔

@Component({
  selector: 'my-menu-item',
  standalone: true,
  imports: [],
  hostDirectives: [CdkMenuItem],
  template: `
    <ng-content></ng-content>
  `,
})
export class MenuItemComponent { }
@Component({
  selector: 'my-menu',
  standalone: true,
  imports: [],
  hostDirectives: [CdkMenu],
  template: `
    <ng-content></ng-content>
  `,
})
export class MenuComponent { }
<button [cdkMenuTriggerFor]="menu">
  Click me!
</button>
<ng-template #menu>
  <my-menu>
    <my-menu-item>Tes M</my-menu-item>
    <my-menu-item>Tes M</my-menu-item>
    <my-menu-item>Tes M</my-menu-item>
  </my-menu>
</ng-template>

Create your select component

<select>
  <option>Option 1</option>
  <option>Option 2</option>
  <option>Option 3</option>
</select>

Other Potential Usecases

  • TestingId (for e2e tests)
  • Analytics
  • Theming (config based theming)
  • Highlight (css change)
  • Keyboard Navigation (behavioural)
  • Card / Menu with Tooltip
  • Attach drag drop behaviour
  • Base Component as a host directive
  • etc.

Advantages

 
  • Cleaner Approach to composing behaviours
  • Static typing works
  • hostDirectives shares same DI context
  • Helps to hide directives API
  • Can easily configure the input and outputs properties

www.pankajparkar.com

@pankajparkar

Limitations

 
  • Execution order matters
  • To many hostDirectives may create performance problem
  • Directive can apply once per element
  • No generics type can be passed to directive
  • Component can not be used as `hostDirectives`

www.pankajparkar.com

@pankajparkar

@pankajparkar

www.pankajparkar.com

Say Hi 👋

Made with Slides.com