195

My Code:

<li *ngFor="let item of list; let i=index" class="dropdown-item" (click)="onClick(item)">
  <template [ngIf]="i<11">{{item.text}}</template>
</li>

I am trying to have only 10 list elements display at any point. As suggested in the answer here, I used ngIf but this results in empty list items (beyond 10) showing up on the page.

4b0
  • 21,981
  • 30
  • 95
  • 142
zer0
  • 4,657
  • 7
  • 28
  • 49

9 Answers9

420

This seems simpler to me

<li *ngFor="let item of list | slice:0:10; let i=index" class="dropdown-item" (click)="onClick(item)">{{item.text}}</li>

Closer to your approach

<ng-container *ngFor="let item of list; i as index">
  <li class="dropdown-item" (click)="onClick(item)" *ngIf="i<11">{{item.text}}</li>
</ng-container>

alternative

<ng-template ngFor let-item [ngForOf]="list" let-i="index">
  <li class="dropdown-item" (click)="onClick(item)" *ngIf="i<11">{{item.text}}</li>
</ng-template>
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 4
    The second approach gives you better flexibility for UI. i.e. *ngIf="i < 11 || showAll" – bryjohns Apr 26 '17 at 02:14
  • 1
    hi, lets say the array has 12 items and we slice 3... how to get remainder of that so that it can be displayed somewhere else (e.g in a button: 9 Items left) – Yasir May 11 '17 at 06:09
  • 1
    Does `slice:0:10` effect the original array?? – Mr.7 Sep 07 '17 at 13:18
  • 21
    `slice` returns a copy and doesn't effect the original array – Günter Zöchbauer Sep 07 '17 at 13:19
  • 17
    The `| slice:0:10` pipe, is great, it helped me to construct a list with a "View more" button that increments the `slices`'s second argument. – Machado Sep 25 '18 at 13:24
  • @Machado I'm also working with a similar procedure. Will increasing the second argument re-render first 10 elements as well? Or will it only render remaining elements and append them? – Pathmila Kariyawasam Sep 30 '19 at 05:12
  • I don't know by heart. You could add some CSS animation (for example border or background color) when an element is added to the DOM and check if the animation is applied again to the first 10 elements if you raise the upper limit. – Günter Zöchbauer Sep 30 '19 at 05:15
  • @PathmilaKariyawasam nope, you must increse first argument as well, e.g.: if first page was 0:10, second page will be 10:20. – Machado Oct 01 '19 at 13:37
37

You can directly apply slice() to the variable. StackBlitz Demo

<li *ngFor="let item of list.slice(0, 10);">
   {{item.text}}
</li>
Nidhin Joseph
  • 9,981
  • 4
  • 26
  • 48
  • just like the name suggests this is good if you don't want any of the remaining items to display hidden taking the space. – techshot nextgen Feb 08 '22 at 03:33
  • this solution is correct but not effective as it leads to performance issues. Check [here](https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496). Use pure pipe (slicePipe) instead. – Abu Taha Jul 01 '22 at 06:53
33

For example, lets say we want to display only the first 10 items of an array, we could do this using the SlicePipe like so:

<ul>
     <li *ngFor="let item of items | slice:0:10">
      {{ item }}
     </li>
</ul>
Manoj Meria
  • 586
  • 6
  • 13
11

You can dynamically limit *ngFor by applying ternary operator.

Example 1 -

<div *ngFor="let item of (showAllFlag ? list : list.slice(0,2))" >
</div>
Vivek Kumar
  • 2,625
  • 2
  • 25
  • 33
  • 1
    Cool suggestion! But if the list is too big, showAll could throw off the users. I personally used a "limit" variable and have a button to expand that limit by 5-10 at a time. – David Thong Nguyen Apr 13 '22 at 04:00
  • @DavidThongNguyen do you have an example how can expand the limit. Are you keeping track of a variable that you increase or do you use an another solution? – Babulaas Jul 25 '23 at 07:32
10

This works very well:

<template *ngFor="let item of items; let i=index" >
 <ion-slide *ngIf="i<5" >
  <img [src]="item.ItemPic">
 </ion-slide>
</template>
Mwiza
  • 7,780
  • 3
  • 46
  • 42
Bembo
  • 1,859
  • 1
  • 13
  • 6
4

In addition to @Gunter's answer, you can use trackBy to improve the performance. trackBy takes a function that has two arguments: index and item. You can return a unique value in the object from the function. It will stop re-rendering already displayed items in ngFor. In your html add trackBy as below.

<li *ngFor="let item of list; trackBy: trackByFn;let i=index" class="dropdown-item" (click)="onClick(item)">
  <template [ngIf]="i<11">{{item.text}}</template>
</li>

And write a function like this in your .ts file.

trackByfn(index, item) { 
  return item.uniqueValue;
}
0

html

<table class="table border">
    <thead>
        <tr>
            <ng-container *ngFor="let column of columns; let i = index">
                <th>{{ column }}</th>
            </ng-container>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let row of groups;let i = index">
            <td>{{row.name}}</td>
            <td>{{row.items}}</td>
            <td >
                <span class="status" *ngFor="let item of row.Status | slice:0:2;let j = index">
                    {{item.name}}
                   </span><span *ngIf = "i < 2" class="dots" (mouseenter) ="onHover(i)" (mouseleave) ="onHover(-1)">.....</span> <span [hidden] ="test" *ngIf = "i == hoverIndex" class="hover-details"><span  *ngFor="let item of row.Status;let j = index">
                    {{item.name}}
                   </span></span>
           </td>

        </tr>
  </tbody>
</table>

<p *ngFor="let group of usersg"><input type="checkbox" [checked]="isChecked(group.id)" value="{{group.id}}" />{{group.name}}</p>

<p><select [(ngModel)]="usersr_selected.id">
   <option *ngFor="let role of usersr" value="{{role.id}}">{{role.name}}</option> 
</select></p>

typescript

import { Component, OnInit } from '@angular/core';
import { CommonserviceService } from './../utilities/services/commonservice.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
  getListData: any;
 dataGroup: FormGroup;
 selectedGroups: string[];
    submitted = false;
    usersg_checked:any;
    usersr_selected:any;
    dotsh:any;
    hoverIndex:number = -1;
    groups:any;
    test:any;
 constructor(private formBuilder: FormBuilder) {


     }
     onHover(i:number){
 this.hoverIndex = i;
}
     columns = ["name", "Items","status"];

public usersr = [{
    "id": 1,
    "name": "test1"

}, {
    "id": 2,
    "name": "test2",

}];


  ngOnInit() {
      this.test = false;
      this.groups=[{
        "id": 1,
        "name": "pencils",
        "items": "red pencil",
        "Status": [{
            "id": 1,
            "name": "green"
        }, {
            "id": 2,
            "name": "red"
        }, {
            "id": 3,
            "name": "yellow"
        }],
        "loc": [{
            "id": 1,
            "name": "loc 1"
        }, {
            "id": 2,
            "name": "loc 2"
        }, {
            "id": 3,
            "name": "loc 3"
        }]
    },
    {
        "id": 2,
        "name": "rubbers",
        "items": "big rubber",
        "Status": [{
            "id": 1,
            "name": "green"
        }, {
            "id": 2,
            "name": "red"
        }],
        "loc": [{
            "id": 1,
            "name": "loc 2"
        }, {
            "id": 2,
            "name": "loc 3"
        }]
    },
    {
        "id": 3,
        "name": "rubbers1",
        "items": "big rubber1",
        "Status": [{
            "id": 1,
            "name": "green"
        }],
        "loc": [{
            "id": 1,
            "name": "loc 2"
        }, {
            "id": 2,
            "name": "loc 3"
        }]
    }

];

      this.dotsh = false;

      console.log(this.groups.length);

this.usersg_checked = [{
    "id": 1,
    "name": "test1"

}, {
    "id": 2,
    "name": "test2",

}];

 this.usersr_selected = {"id":1,"name":"test2"};;

this.selectedGroups = [];
this.dataGroup = this.formBuilder.group({
            email: ['', Validators.required]
});
  }

  isChecked(id) {
   console.log(this.usersg_checked);
  return this.usersg_checked.some(item => item.id === id);
}
 get f() { return this.dataGroup.controls; }
onCheckChange(event) {
  if (event.target.checked) {
 this.selectedGroups.push(event.target.value);
} else {
 const index = this.selectedGroups.findIndex(item => item === event.target.value);
 if (index !== -1) {
  this.selectedGroups.splice(index, 1);
}
}
}

   getFormData(value){
      this.submitted = true;

        // stop here if form is invalid
        if (this.dataGroup.invalid) {
            return;
        }
      value['groups'] = this.selectedGroups;
      console.log(value);
  }


}

css

.status{
    border: 1px solid;
    border-radius: 4px;
    padding: 0px 10px;
    margin-right: 10px;
}
.hover-details{
    position: absolute;


    border: 1px solid;
    padding: 10px;
    width: 164px;
    text-align: left;
    border-radius: 4px;
}
.dots{
    position:relative;
}
ankush
  • 121
  • 1
  • 12
0
 <div *ngFor="let item of list;trackBy: trackByFunc" >
   {{item.value}}
 </div>

In your ts file

 trackByFunc(index, item){
    return item ? item.id : undefined;
  }
0

I found that below is the good approach

<ul class="list-group col-auto" *ngFor="let result of searchResults.slice(0,10)">
                    <li class="list-group-item">
                        <b>{{result.name}}</b>
                        <p>{{result.email}}</p>
                        <p>{{result.body | slice:0:64}}</p>
                    </li>
                </ul>
Sreehari Ballampalli
  • 3,404
  • 3
  • 12
  • 19