Why ng-content selector is not working inside *ngFor

前端 未结 6 1091
梦如初夏
梦如初夏 2020-12-21 18:06

Here is the stackblitz code.

As you can see

相关标签:
6条回答
  • 2020-12-21 18:37

    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.

    0 讨论(0)
  • 2020-12-21 18:47

    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>
    
    0 讨论(0)
  • 2020-12-21 18:53

    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

    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

    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

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

    0 讨论(0)
  • 2020-12-21 18:56

    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>
    
    0 讨论(0)
  • 2020-12-21 18:57

    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.

    0 讨论(0)
  • 2020-12-21 18:57

    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.

    0 讨论(0)
提交回复
热议问题