2

Here is the stackblitz code.

As you can see

<div *ngFor="let number of numbers;let i = index">
        <div *ngIf="(number%2 !== 0);else even">
            <ul odd>{{number}}</ul>
        </div>
    <ng-template #even>
      <ul even>{{number}}</ul>
    </ng-template>    
</div>

I am trying to render values using ng-content selectors but it isn't working inside *ngFor loop. Why so ? How to make it work ? More like this img

I don’t want two ngFor loop, I am looking to make it work with ng-content.

Shashank Vivek
  • 16,888
  • 8
  • 62
  • 104
  • @TarunLalwani : Thank you so much. I was waiting for some other answers before selecting yours. Up-voted your answer as well :). I already applied the workarounds which you have proposed. Selected your answer for confirming that `ng-content` wont work as I expect. – Shashank Vivek May 03 '18 at 06:03

6 Answers6

7

You cannot do this, without two loops. The reason being ng-content is a placeholder, which accepts dynamic html inside. Now when you loop inside your code, you don't assign anything to the even or odd selector. But if you change your code to below

<child>
    <div *ngFor="let number of numbers;let i = index" even>
        <div *ngIf="(number%2 !== 0);else even">
            <ul>{{number}}</ul>
        </div>
    <ng-template #even>
      <ul>{{number}}</ul>
    </ng-template>    
  <!-- <ul even>Why its rendering and not others??</ul> -->
    </div>
</child>

You will see the output change to

Change

This is because no the output of ng-For has been assigned to the even selector.

If you change the template to below

<child>
    <div *ngFor="let number of numbers;let i = index" odd>
        <div *ngIf="(number%2 !== 0)">
            <ul>{{number}}</ul>
        </div>
    </div>
    <div *ngFor="let number of numbers;let i = index" even>
    <div *ngIf="(number%2 == 0)">
      <ul>{{number}}</ul>
    </div>    
    </div>
</child>

It will work

Working

Also remember inside a ng-For you cannot append content to a even or odd section, even if that feature was available it would have overwritten the section with the last value.

Leaving even ng-for out of it. The below code

<child>
  <div>
    <div odd>
      This should be under odd
    </div>
  </div>
</child>

Also won't work

Not working

So what you are trying to achieve and the way you are trying to achieve it is not supported by angular.

Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265
3

You can do it this way with two loops:

<div>
    <ng-container *ngFor="let number of numbers; let isFirst=first">
        <h2 *ngIf="isFirst">Odd Numbers</h2>
      <p>{{(number%2>0? number : '')}}</p>
   </ng-container>

  <ng-container *ngFor="let number of numbers; let isFirst=first">
      <h2 *ngIf="isFirst">Even</h2>
      <p>{{(number%2==0? number : '')}}</p>
  </ng-container>
</div>
Farasi78
  • 981
  • 10
  • 18
  • Maybe you can get an idea from this: https://stackoverflow.com/questions/36755844/angular2-child-component-as-data/36760027#36760027 – Farasi78 Apr 25 '18 at 02:23
2

One of the solutions is to sort your numbers array. You can use your own pipe:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({name: 'oddFirst'})
export class OddFirstPipe implements PipeTransform {

  transform(numbers: number[], args: any[]): number[] {
    const odd = numbers.filter(num => num % 2 !== 0);
    const even = numbers.filter(num => num % 2 === 0);
    return [...odd, ...even];
  }

}

Then you can use it in your template:

<div *ngFor="let number of numbers | oddFirst">
        <div *ngIf="(number%2 !== 0);else even">
            <ul odd>{{number}}</ul>
        </div>
    <ng-template #even>
      <ul even>{{number}}</ul>
    </ng-template>    
</div>
matejko219
  • 1,601
  • 10
  • 14
  • What about `

    ` tag which I am rendering as well ? I dont want sorted list, so I can implement the same *ngIf and have it. I am not looking for this solution :(

    – Shashank Vivek Apr 26 '18 at 08:11
2

You can do it with pure CSS.

Take a look at the modified StackBlitz.

https://stackblitz.com/edit/angular-zto8w1?file=app/app.component.html

CSS display grid works wonders. Keep in mind it's not (fully) supported on IE.

Roberto Zvjerković
  • 9,657
  • 4
  • 26
  • 47
2

It's because of nesting.

I ran some experiments on stackblitz and it seems that the contents cannot be nested at all and we just cannot blame the *ngFor for it. Even using ng-container on the for loop did not help.

kvetis
  • 6,682
  • 1
  • 28
  • 48
0

First lets see what projection/ngContent is.
Projection is a very important concept in Angular. It enables developers to build reusable components and make applications more scalable and flexible.

In more simple words it helps in adding content dynamically which can come from server as html or text inside the component without creating DOM node like document.createtextnode().

Refrence for projection: https://angular-2-training-book.rangle.io/handout/components/projection.html

Projection is similar to angularjs transclusion in directives. https://docs.angularjs.org/api/ng/directive/ngTransclude

Lets jump to your problem.
That's right we need two loops for repeating content in Odd and even numbers acc. to your requirement. reason: ngFor adds new instance of same template in DOM.

Working code
https://stackblitz.com/edit/angular-ymznfu?file=app%2Fapp.component.html

Example:

App.component.html

<child>
    <ng-container odd>//this will work
     <ul>{{number[1]}}</ul>
    </ng-container>
 </child>

child.component.html

<ng-content select="[odd]"></ng-content>

Above example will work reason, We are projecting outermost dom node in ngContent.

You can try projecting below code

app.component.html

 <child>
        <ng-container>
            <ng-content select="[odd]"></ng-content>
        </ng-container>
    </child>

above code will not project reason Angular checks only the outermost node available in the component i.e the reason in your code

<ul odd>Something</ul>

is projecting and others not.

harsh
  • 247
  • 1
  • 2
  • 12