5

I am trying to develop a carousel.

The desired end result should be that the developer would simply write the entire markup in one place (let's say in app.component.html) with only an options property and then, the carousel would take over.

Problem is that from carousel.component I need to set some properties on carousel-item.component (properties that app.component should have nothing to do with... but all the markup is in app.component.html).

How can I achieve this?

app.component.html:

<carousel [options]="myOptions">
    <carousel-item *ngFor="let item of items">
        <img [src]="item.image" alt="" />
    </carousel-item>
</carousel>

<hr />

<carousel [options]="myOptions2">
    <carousel-item *ngFor="let item of items">
        <img [src]="item.image" alt="" />
    </carousel-item>
</carousel>

carousel.component.html:

<div class="carousel-stage">
    <ng-content></ng-content>
</div>

carousel-item.component.html:

<ng-content></ng-content>
MrCroft
  • 3,049
  • 2
  • 34
  • 50

1 Answers1

4

I think the only solution is to go with @ContentChildren()

In my carousel.component.ts:

import { ContentChildren, ... } from '@angular/core';

// ...

export class CarouselComponent implements AfterContentInit {
  @ContentChildren(ItemComponent) carouselItems;

  ngAfterContentInit() {
    this.carouselItems.forEach((item: ItemComponent, currentIndex) => {
      // Do stuff with each item
      // Even call item's methods:
      item.setWidth(someComputedWidth);
      item.setClass(someClass);
    }
  }
}

Then, in carousel-item.component.ts:

export class ItemComponent implements OnInit, OnDestroy {
  @HostBinding('style.width') itemWidth;
  @HostBinding('class') itemClass;
  @HostBinding('@fade') fadeAnimationState;


  setWidth(width) {
    this.itemWidth = width + 'px';
  }
  setClass(class) {
    this.itemClass = class;
  }
  setAnimationState(state) {
    this.fadeAnimationState = state;
  }
}

Apparently, I can even bind animation triggers with @HostBinding. I assumed @HostBingind() was designed to only work with the standard html attributes (style, class etc.), but it seems that I can actually bind anything (literally anything).

Does anybody have a better solution? Before I accept my own answer...

MrCroft
  • 3,049
  • 2
  • 34
  • 50
  • 1
    Thanks for sharing this. This is the right way to do it, IMO. For another somewhat similar reference, you can look at how the Ag-Grid team has used transclusion for column configurations: [ag-grid-angular component](https://github.com/ag-grid/ag-grid-angular/blob/master/src/agGridNg2.ts), [ag-grid-column component](https://github.com/ag-grid/ag-grid-angular/blob/master/src/agGridColumn.ts). `@ContentChildren` is used without `ng-content` to get and use the child elements for configuration. – Mike Hill Aug 31 '17 at 15:44