19

How do I pass the current variable in an ngFor loop to ngIf, if it is using templates with then/else syntax?

It appears that they pass through fine when used inline, but aren't accessible from a template, for example:

<ul *ngFor="let number of numbers">
  <ng-container *ngIf="number % 2 == 0; then even_tpl; else odd_tpl"><>/ng-container>
</ul>


<ng-template #odd_tpl>
  <li>Odd: {{number}}</li>  
</ng-template>

<ng-template #even_tpl>
  <li>Even: {{number}}</li>  
</ng-template>

The templates don't seem to have access to number at all, but it works if used inline.

A full example of the working and not-working versions in the following link: plunkr

match
  • 5,496
  • 1
  • 15
  • 32

5 Answers5

24

All you need is to use [ngTemplateOutletContext] Read More

Here is the way how you can achieve that :

<div>
  <h3>All Templates</h3>
  <ul *ngFor="let number of numbers">
    <ng-container [ngTemplateOutlet]='number % 2 == 0 ? even_tpl : odd_tpl' [ngTemplateOutletContext]="{number:number}"></ng-container>
  </ul>
</div>

<ng-template #odd_tpl let-number='number'>
  <li>Odd: {{number}}</li>  
</ng-template>

<ng-template #even_tpl let-number='number'>
  <li>Even: {{number}}</li>  
</ng-template>

WORKING DEMO

Vivek Doshi
  • 46,471
  • 9
  • 84
  • 100
  • 2
    Thanks - I'd seen the docs on `ngTemplateOutlet` but hadn't managed to grok it into this situation - seeing a working example makes it clear how to use it! – match Jan 11 '18 at 11:42
  • 2
    @match, Glad you figure it out , Its happens sometimes :) , happy coding. – Vivek Doshi Jan 11 '18 at 12:29
2

look here: Pass variable in Angular 2 template

<div *ngFor="foo in foos">
    <ng-container *ngTemplateOutlet="inner; context:{name:foo}"></ng-container>
</div>
<ng-template #inner let-name="name">
    {{ name }}
</ng-template>
Vega
  • 23,736
  • 20
  • 78
  • 88
Guntram
  • 831
  • 13
  • 18
  • Here's a plunker: https://stackoverflow.com/questions/40418598/cant-get-ngtemplateoutlet-to-work – Guntram Jan 11 '18 at 11:31
1

It's because the way template scope works. Templates in Angular have dynamic scope instead of regular javascript lexical scope, that means, the {{ number }} expression inside the ng-template is not pointing to the same number variable in your *ngFor, although one should think it would since you're kinda evaluating the template expression where <ng-container> is.

If you actually define a number property in your AppComponent, let's say number = 42, you can see that all the {{number}} expressions inside <ng-template> evaluates to 42.

So either you should define your templates inside the scope of the *ngFor, where the number variable has the desired value, or somehow pass this value to both even_tpl and odd_tpl. As @Vivek has pointed out, you can do this with ngTemplateOutlet and ngTemplateOutletContext.

Osman Cea
  • 1,287
  • 7
  • 17
0

Here are couple more (similar) options for multiple arguments, this one includes using 'ngTemplateOutletContext' and also a condition (in 4th argument - for fun).

... should work by copy and paste ...

            <!-- DEMO using: 
                "=templateID; context:{prop:value, ...}"
                ( 4 arguments demo)
                Note: $implicit identifies the default argument in the template.
                        The template does not need to assign the argument name,
                        - see the 3rd argument
            -->
    <div *ngFor="let item of ['Aaa', 'Bbb', 'Ccc']; index as ix">
        <ng-container *ngTemplateOutlet="myTemplate1; 
                    context:{cDemoName:'Option 1:',
                             cIx:ix+1, 
                             $implicit:item, 
                             cIsEven: ((ix % 2) === 0) ? 'true' : 'false' }">
        </ng-container>
    </div>

    <hr>

            <!-- DEMO using: 
                [ngTemplateOutlet]="templateID"
                [ngTemplateOutletContext]="{"=templateID; context:{prop:value, ...}"
            -->

    <div *ngFor="let item of ['Dddd', 'Eee', 'Fff']; index as ix">
        <ng-container [ngTemplateOutlet]="myTemplate1"
                      [ngTemplateOutletContext]="{
                        cDemoName:'Option 2',
                        cIx:ix+1, 
                        $implicit:item, 
                        cIsEven: ((ix % 2) === 0) ? 'true' : 'false' }
                    ">
        </ng-container>
    </div>

            <!-- DEMO template: 
                ( 4 arguments expected)
            -->
    <ng-template #myTemplate1 
            let-cDemoName="cDemoName"
            let-cIx="cIx"
            let-cItem
            let-cIsEven="cIsEven">

        Context arguments demo name: {{cDemoName}} <br>
        . . . . . Context index: {{cIx}} <br>
        . . . . . Context item: --- {{ cItem }} --- <br>
        . . . . . Context is-even: {{ cIsEven }} <br>
        <br>
    </ng-template>
Felix
  • 1,102
  • 1
  • 8
  • 20
0

All answers use the same name for key-value, So at the first look, it makes me confuse.

Here working example for Angular 10

<div *ngFor="let data of dataList">
       <ng-container *ngTemplateOutlet="data.istruevalue ? truevalue : falsevalue ; context: { passdata:data}"></ng-container>
</div>

<ng-template #truevalue let-info="passdata">    
        <label >true : {{info.name}}</label><br>
</ng-template>

<ng-template #falsevalue let-info="passdata">   
        <label >false : {{info.name}}</label><br>
</ng-template>
Jze
  • 1,692
  • 3
  • 14
  • 39