Pass Component to a Component in Angular

angular, components, input, nested, typescript

Solution

You should not use an `@Input` to pass in the components. You can use `@ContentChildren` for that and an abstract `WidgetComponent`:

@Component({
    selector: 'moving-grid',
    template: `
      <div class="widget-wrapper">
         <ng-container *ngFor="let widget of widgets">
             <!-- use a ngSwitchCase here for different types-->
             <grid-test-widget [widget]="widget" *ngIf="widget.active && widget.type === 'test'"></grid-widget>
         </ng-container>          
      </div>
    `,
    styleUrls: ['./grid.component.css']
})
export class GridComponent implements AfterContentInit {
     @ContentChildren('widget')
     widgets: QueryList<WidgetComponent>;

     ngAfterContentInit() {
        //your components will be available here
     }
}

The template of your third component `[moving-grid-container]` will stay the same, but without the `[widgets]` and an added `#widget` name:

<moving-grid> 
  <test-widget class="grid-item-content-001" #widget [active]="false"></test>
  <test-widget class="grid-item-content-002" #widget></test>
</moving-grid>

Your `TestWidgetComponent` which will extend an abstract WidgetComponent :

@Component({
  selector: 'test-widget',
  // ...
})
export class TestWidgetComponent extends WidgetComponent {
    public type: string = 'test';
}

And your `WidgetComponent`:

@Directive()
export abstract class WidgetComponent {

   @Input()
   public active: boolean;

   public type: string;

}

And you'll have several grid widgets based on the type of the widget:

@Component({
  selector: 'grid-test-widget',
  template: `<div #content>I say hello<li>i</li><li>u</li><button (click)="test()">click me</button> world</div>`
})
export class GridTestWidgetComponent{}
    

Problem

I have this small gridComponent: ``` @Component({ selector: 'moving-grid', templateUrl: './grid.component.html', styleUrls: ['./grid.component.css'] }) export class GridComponent { @Input('widgets') extComponents: Array<Component>; } ``` And a second testComponent ``` @Component({ selector: 'test', template: ` <div #content>I say hello<li>i</li><li>u</li><button (click)="test()">click me</button> world</div> ` }) export class TestComponent { @ViewChild('content') content: HTMLElement; getContent() { return this.content; } test() { console.log("test"); } } ``` Now I'm trying to pass multiple instances of testComponent to the gridComponent. Therefore I have a third Component which looks like this one: ``` selector: 'moving-grid-container', template: ` <moving-grid [widgets]="[z1,z2]"> <test class="grid-item-content-001" #z1></test> <test class="grid-item-content-002" #z2></test> </moving-grid> ` ``` Until this point, everything works like expected. But how can I render the Components from @Input in the gridComponent? My first approach was to declare a @ViewChild in the testComponent and return it with a getContent()-function. But it won't work. Can I use the ng-content directive in some way or is there a better solution? The GridComponent looks like this. I want to display the templates of a @Input-Component inside one of the black boxes. Is it possible? Thanks for any kind of help

Original source