21

Angular's NgTemplateOutlet allows you to pass a context to the outlet for property binding.

<ng-container *ngTemplateOutlet="eng; context: {$implicit: 'World'}"></ng-container>
<ng-template #eng let-name><span>Hello {{name}}!</span></ng-template>

Angular's *ngIf allows you to embed one template or another based on a boolean condition:

<ng-container *ngIf="isConditionTrue; then one else two"></ng-container>
<ng-template #one>This shows when condition is true</ng-template>
<ng-template #two>This shows when condition is false</ng-template>

How can I pass context to these templates referred to within the *ngIf syntax?

BeetleJuice
  • 39,516
  • 19
  • 105
  • 165
  • 1
    I don't think that is possible. I've seen other people with the same problem and they all ended up using ngTemplateOutlet – JiiB Feb 15 '18 at 15:18

2 Answers2

28

Actually you can input your condition into ngTemplateOutlet (and get rid of ngIf).

<ng-container *ngTemplateOutlet="condition ? template1 : template2; context: {$implicit: 'World'}">
</ng-container>
Sielu
  • 1,070
  • 14
  • 19
  • Hey; I don't remember now how I solved this problem back in February, but I like your solution. – BeetleJuice Jun 24 '18 at 23:59
  • Awesome when we use it inside an `ngFor` loop and pass the iterated item as `$implicit` context. We can then reuse ``s from outside of the loop. – Lars Gyrup Brink Nielsen Sep 04 '18 at 08:22
  • the use case where this will not help is when using `*ngIf="observable$ | async as data; else loadingTemplate` and you want to pass what you want to load e.g. "Loading my awesome data" – chaimm Feb 03 '21 at 17:00
  • @chaimm you're right. You'd need to wrap my code in which would first resolve the async value – Sielu Feb 05 '21 at 10:51
  • @Sielu that would not help either since what the else template you are trying to show is only as long as the async value is not resolve, for example a loader template that says dynamically what is being loaded. so as long as the async value is not resolved you would not get into the `ng-container` – chaimm Feb 05 '21 at 19:25
  • Yes, you are right. I misunderstood you before. – Sielu Feb 07 '21 at 07:48
4

The accepted answer works when you have a single expression but will get complex where you have multiple ngIf expressions and templates.

A full example for cases where you have multiple expressions, resulting templates and even different context variables:

<!-- Example ngFor -->
<mat-list-item
    *ngFor="let location of locations$; let l = index"
    [ngSwitch]="location.type"
    >
    <!-- ngSwitch could be ngIf on each node according to needs & readability -->

    <!-- Create ngTemplateOutlet foreach switch case, pass context -->
    <ng-container *ngSwitchCase="'input'">
        <ng-container 
            *ngTemplateOutlet="inputField; context: { location: location, placeholder: 'Irrigation Start', otherOptions: 'value123' }">
        </ng-contaier>
    </ng-container>

    <ng-container *ngSwitchCase="'select'">
        <ng-container 
            *ngTemplateOutlet="selectField; context: { location: location, selectSpecificOptions: 'scope.someSelectOptions' }">
        </ng-contaier>
    </ng-container>

    <!-- ngSwitchCase="'others'", etc. -->

</mat-list-item>



<!-- Shared ngTemplates & note let-[variable] to read context object into scope -->
<ng-template 
    #inputField 
    let-location="location"
    let-placeholder="placeholder
    let-otherOptions="otherOptions"
    <!-- Context is now accessible using let-[variable] -->
    INPUT: {{ location.value }} {{ placeholder }} {{ otherOptions }}
</ng-template>

<ng-template 
    #selectField 
    let-location="location"
    let-options="selectSpecificOptions"
    <!-- Context is now accessible using let-[variable] -->
    SELECT: {{ location.value }} {{ options }}
</ng-template>

Where;

location$ = [
    {type: 'input',  value: 'test'}, 
    {type: 'input',  value: 'test 2'}, 
    {type: 'select', value: 'test 3'}
]; 
Ralpharoo
  • 789
  • 9
  • 21