Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ContentChildren with multiple content types

Tags:

angular

Hello I am currently building a table that will allow multiple column types in it. I want to be able to use this like:

<my-table [rows]="rows">
     <text-column [someParameters]="here" header="text"></text-column>
     <icon-column [someParameters]="here" header="icon"></icon-column>
</my-table>

text-column and icon-column are separate directives.

I currently have an abstract class called column and lets say the text-column and the icon-column may look something like:

   export abstract class Column
   {
       @Input() someParameters:string;
       @Input() header:string;
   }

   export class TextColumnDirective extends Column
   {
      //I do cool stuff here
   }

   export class IconColumnDirective extends Column
   {
      //I do different cool stuff   
   }

My table may look something like:

@Component({
   selector:'my-table',
   template: `
        <table>
            <thead>
                <tr>
                   <th *ngFor="let column of columns">{{column.header}}</th>
                </tr>
            </thead>
        </table>
   `
})
export class MyTableComponent
{
    @ContentChildren(Column) columns:QueryList<any>;

    //I do cool stuff too
}

So this approach works if I do not use an abstract and just call it with just text column like @ContentChildren(TextColumnDirective) columns:QueryList<any>; but only gets the text column and the same with the icon column. How can I accomplish this where I can add different types of directives for different columnTypes later?

like image 694
3xGuy Avatar asked Mar 14 '18 13:03

3xGuy


1 Answers

The answer from @3xGuy is correct but the forwardRef(() => {}) is not required unless the type being provided is defined after the decorator or the decorated class see this Angular In Depth post

Note this approach can be used for ContentChildren or ViewChildren, below I use ViewChildren

item.ts

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

export class Item {
  color = '';
}

@Directive({
  selector: '[blueItem]',
  providers: [{ provide: Item, useExisting: BlueItemDirective }],
})
export class BlueItemDirective { // 'extends Item' is optional
  color = 'blue';
}

@Directive({
  selector: '[redItem]',
  providers: [{ provide: Item, useExisting: RedItemDirective }],
})
export class RedItemDirective { // 'extends Item' is optional
  color = 'red';
}

app.component.ts

import { Component, ViewChildren, QueryList } from '@angular/core';

import { Item } from './item';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Multiple View Child Types';

  // Note we query for 'Item' here and not `RedItemDirective'
  // or 'BlueItemDirective' but this query selects both types
  @ViewChildren(Item) viewItems: QueryList<Item>;

  itemColors: string[] = [];

  ngAfterViewInit() {
    this.itemColors = this.viewItems.map(item => item.color);
  }
}

app.component.html

<div redItem>Item</div>
<div blueItem>Item</div>
<div blueItem>Item</div>
<div blueItem>Item</div>
<div redItem>Item</div>

<h2>Colors of the above directives</h2>
<ul>
  <li *ngFor="let color of itemColors">{{color}}</li>
</ul>

Here is a StackBlitz showing this behavior in action.

like image 70
seangwright Avatar answered Sep 18 '22 07:09

seangwright