mkHun
mkHun

Reputation: 5927

How to pass one component selector to another component?

I am trying to create the global component, so other developers can use this component to their project.

Here the aim is, I have my local component called app.component.ts and it has one child component. And I have bundled the global component and kept inside the node_modules folder.

app.component.html

<p-table #dt [value]='genes' [columns]="tableHeader" dataKey="geneName">

    <ng-template pTemplate="header">
        <th></th>
        <th>Gene</th>
        <th>Position</th>
        <th>Value</th>
    </ng-template>

    <ng-template pTemplate="body"  let-columns="columns" let-expanded="expanded" let-gene>
            <tr [pSelectableRow]="gene">
            ...
            </tr>
    </ng-template>

    <ng-template pTemplate="rowexpansion" let-rowData let-columns="columns">
        <tr>
            <gene-child [geneData]="rowData" (geneSelection)="selection()"> </gene-child>
        </tr>
    </ng-template>

</p-table>

child.component.ts

import { ConfirmationService } from 'primeng/api';
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';

@Component({
    selector: 'gene-child',
    templateUrl: './gene.html',
})
export class GeneChildComponent implements OnInit {
    @Input() geneName: any;
    @Output() geneSelection =  new EventEmitter(); 

Above code is working fine without any issue.


Now I am creating the global component to handle the priemng table, from the parent component just I need to pass the headers and data.

updated code

app.component.html

<bio-tables [tableData]="genes" [header]="tableHeader"></bio-tables>

above code also working fine.


But here my problem is I need to pass the "selector" gene-child to the global component so I did the following, but it is giving the error,

<bio-tables [tableData]="genes" [header]="tableHeader" [rowExpansionSelector]="gene-child"></bio-tables>

I tried to storing the gene-child component into one variable and tried passing it, like below code

app.component.ts

import { GeneChildComponent } from './gene-child.component.ts';
...

export class ....{

    geneChlid = GeneChildComponent;

app.component.html

<bio-tables [tableData]="genes" [header]="tableHeader" [rowExpansionSelector]="geneChild"></bio-tables>

above code also not working. I am not sure how to pass the selector to the child component.

Upvotes: 1

Views: 2649

Answers (2)

Oscar Ludick
Oscar Ludick

Reputation: 782

From what i understood, you want to pass a dynamic component to your bio-tables component, if is that so, you could try do the following:

Create and abstract class let´s call it GenericChild in here define what do you expect from component.

export abstract class GenericChild {
  abstract geneName: any;
  abstract geneSelection: EventEmitter<any>;
}

Make the GeneChildComponent or whatever component you want, to extends from this abstract class.

export class GeneChildComponent extends GenericChild {
  @Input() geneName: any;
  @Output() geneSelection = new EventEmitter<any>();
}

We will create a wrapper component so we can access to let-rowData, this wrapper will create a child component (GeneChildComponent that is the dynamic component). For this we need a ng-contaier as ViewContainerRef and also we will extend from GenericChild.

To create the component we will use ComponentFactoryResolver.

The template:

<ng-container #child></ng-container>

The component:

export class ChildWrapper extends GenericChild {
  @Input()
  genericChild: Type<GenericChild>;
  @Input()
  geneData: any;
  @Output()
  geneSelection: EventEmitter<any> = new EventEmitter<any>();

  @ViewChild("child", { read: ViewContainerRef })
  childsContainer: ViewContainerRef;

  constructor(
    private factory: ComponentFactoryResolver,
    private cdr: ChangeDetectorRef
  ) {
    super();
  }
   
  //create the component in the container, and set the values of the instance.
  ngAfterViewInit(): void {
    const componentFactory = this.factory.resolveComponentFactory(
      this.genericChild
    );
    const ref = this.childsContainer.createComponent(componentFactory);

    ref.instance.geneData = this.geneData;
    ref.instance.geneSelection.subscribe(event => {
      this.geneSelection.emit(event);
    });
    this.cdr.detectChanges();
  }
}

Replace the template in your main component with something like the following, and create an @Input to pass the dynamic component (genericChild).

     <ng-template pTemplate="rowexpansion" let-rowData>
      <tr>
        <child-wrapper
          [genericChild]="genericChild"
          [geneData]="rowData"
          (geneSelection)="selection($event)"
        ></child-wrapper>
      </tr>
    </ng-template>

Then all you need to do is:

 //app.component.html
<bio-tables [genericChild]="geneChildComponent"></bio-tables>
 //app.component.ts
geneChildComponent: Type<GeneChildComponent> = GeneChildComponent;

Full example here

Upvotes: 1

Andrei
Andrei

Reputation: 12196

you can pass the template and then render it however you want

app.component.html
<bio-tables ... [rowExpansionTpl]="geneTpl">
</bio-tables>
<ng-template #geneTpl let-params="params">
  <gene-child [geneName]="params.gene" (geneSelection)="selection(params)"> </gene-child>
</ng-template>
bio-tables.component.html
<div>
  <ng-container *ngTemplateOutlet="rowExpansionTpl; context: {params: {gene: 'anything'}}"></ng-container>
</div>

Upvotes: 3

Related Questions