In my Angular 6 app I need to pass a Component to another Component as its ng-template.
The reason is that I have a Component A that I need to replicate many times, but each time it has to include different components (let's call them Component B and Component C) which have the same Inputs.
Component A template:
<div class="row-detail-panel">
<h4 class="row-detail-panel-title">{{ newEntity ? 'Add new' : 'Edit this'}} {{ entityName }}</h4>
<!--THIS IS THE COMPONENT I WANT TO INJECT-->
<app-component-b
[inline]="true"
[form]="form"
></app-component-b>
<!--END-->
<!--some more html code here-->
</div>
And I create a Component A instance using:
<app-component-a
[entity]="row"
[entityName]="entityName"
></app-component-a>
So I thought about using ng-template
, so changing the Component A template as follows:
<div class="row-detail-panel">
<h4 class="row-detail-panel-title">{{ newEntity ? 'Add new' : 'Edit this'}} {{ entityName }}</h4>
<ng-template></ng-template>
<!--some more html code here-->
</div>
And creating a Component A instance using:
<app-component-a
[entity]="row"
[entityName]="entityName"
>
<app-component-b
[inline]="true"
[form]="form" <!--PROBLEM: "form" does not exist here-->
></app-component-b>
</app-component-a>
So I can easily inject Component C instead of Component B as Component A's ng-template:
<app-component-a
[entity]="row"
[entityName]="entityName"
>
<app-component-c
[inline]="true"
[form]="form" <!--PROBLEM: "form" does not exist here-->
></app-component-c>
</app-component-a>
PROBLEM:
the variable form
that I need to inject to Component B or Component C exists only inside Component A and not in Component A's parent (for some reasons I cannot move it one level up).
How can I solve this problem?
What you can do is this:
When you call component A, you pass an ng-template to that as follows:
<app-component-a>
<ng-template *ngIf=”condition; else elseBlock”>
<app-component-b></app-component-b>
</ng-template>
<ng-template #elseBlock>
<app-component-c></app-component-c>
</ng-template>
</app-component-a>
Now in your app-component-a.ts you do this:
@ContentChild(TemplateRef) template: TemplateRef;
So basically template will get component b or c based on your condition.
And then in component A template, you do this:
<ng-container [ngTemplateOutlet]="template"></ng-container>
So now your ng-container will get Component B or C based on your condition.
As far as your form is concerned, i'm afraid the only thing I can think of is to create a service and provide it in component A, inject it in A,B and C and share the form in that service.
But if you include component B and C the way I have shown above, Angular will handle creation and destruction of your B and C components on it's own.
Otherwise when your ng-template condition changes, your component B will not be destroyed when component C is instantiated.
Edit:
One more thing I can think of is that if you are not calling component B or C as soon as A is instantiated, you could also emit (@Output) form from A to A's parent oninit of A. This way when B or C is called, A's parent will have access to form and it can pass it to B or C.