angular: how to loop for numbers?
Asked Answered
O

10

54

I have a collection of items, where each item have a rank attribute, which is a number. I wan to loop over this number, here is what I've tried:

<div class="col-md-3 col-sm-4 item-container" *ngFor="let item of items">
    <div class="item">
        <p class="rank">
            <img src="" class="img-responsive" *ngFor="let rank of item.rank"/>
        </p>
    </div>
</div>

typescript:

export class MonthpicksComponent{

    items: Item[] = [];
    //...
}

export class Item{
  id: number;
  name: string;
  img: string;
  desc: string;
  rank: number;
  voters: number;
}

but unlucky the first loop shows only one result and the second loop showed nothing.

Osmious answered 18/10, 2017 at 7:57 Comment(7)
You'll probably need to add code for how the items object looks like. Html is not sufficient enough to solve this.Synapse
@Synapse check update pleaseOsmious
Provide items structure in controller @mohammadTayib
You cannot loop over a number if its not an array. rank is a number.Synapse
@Synapse I want to print <img> tags as how much rank the item has, how I can do that?Osmious
this is a high frequency question here, please refer to : #36536129Zendah
Possible duplicate of Repeat HTML element multiple times using ngFor based on a numberTiphane
G
53

You can use javascript inside *ngFor so the solution could look like this:

my.component.ts

counter(i: number) {
    return new Array(i);
}

my.component.html

<li *ngFor='let in of counter(5) ;let i = index'>{{i}}</li>
Garratt answered 18/10, 2017 at 8:12 Comment(3)
Doesn't that rebuild on every check, because it's an impure call?Evangelineevangelism
I don't think so, maybe. Easy to check that. The point of that answer is that you can execute js code from *ngFor body. The rest of the answer is just an example. There are literally thousands of ways to do that.Garratt
Sure, but my impression was that executing arbitrary functions or code in an Angular template is a huge anti-pattern because Angular cannot determine whether the code is pure or impure. That's what pipes are for.Evangelineevangelism
S
115

Maybe the simplest solution without any TS code:

<div *ngFor="let item of [].constructor(10); let i = index">
   {{i}}
</div>
Spue answered 5/2, 2019 at 8:30 Comment(2)
Why isn't this the preferred solutions, as this is clearly exactly what most of us have in mind? Like – it certainly does require a temporary array to loop over, but in this case generated without a single line typescript. Kudos!Selaginella
If you prefer to use the more recent notation this would be @for (item of [].constructor(10); track i) {...}.Palatine
G
53

You can use javascript inside *ngFor so the solution could look like this:

my.component.ts

counter(i: number) {
    return new Array(i);
}

my.component.html

<li *ngFor='let in of counter(5) ;let i = index'>{{i}}</li>
Garratt answered 18/10, 2017 at 8:12 Comment(3)
Doesn't that rebuild on every check, because it's an impure call?Evangelineevangelism
I don't think so, maybe. Easy to check that. The point of that answer is that you can execute js code from *ngFor body. The rest of the answer is just an example. There are literally thousands of ways to do that.Garratt
Sure, but my impression was that executing arbitrary functions or code in an Angular template is a huge anti-pattern because Angular cannot determine whether the code is pure or impure. That's what pipes are for.Evangelineevangelism
E
16

If you want to implement this in the canonical Angular way, you could create a range pipe which produces the array on the spot.

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

@Pipe({
  name: 'range'
})
export class RangePipe implements PipeTransform {
  transform(length: number, offset: number = 0): number[] {
    if (!length) {
      return [];
    }
    const array = [];
    for (let n = 0; n < length; ++n) {
      array.push(offset + n);
    }
    return array;
  }
}

And then you would use it in a template like this:

<ul>
  <li *ngFor="let n of item.rank | range">
    <img src="star.png">
  </li>
</ul>

The offset parameter is useful when you don't want the array to start at 0, of course.

A massive upside of this approach is that this is a pure pipe, meaning it will only be executed if item.rank changes. Other approaches may update spuriously on any arbitrary page check phase (potentially many times per second).

Evangelineevangelism answered 10/1, 2020 at 11:9 Comment(3)
Not sure why this doesn't have more up votes. When passing a number into my component via @Input() every other way described here left me with an Invalid array length error in my template. This was the only thing that worked for me.Jacqulynjactation
I'm glad I could be of help! My answer is fairly new, perhaps that's why it doesn't rank high. I'm convinced it's the most Angular-spirited solution.Evangelineevangelism
this is the most optimal solution! Thank you for sharing. I did not realize how simple this was to implement.Yap
F
6

With object

TS file

page = {
  count: 3
}

HTML file

 <div *ngFor="let item of [].constructor( page.count ); let i = index">
   index: {{i}} | other content...
 <div>
Fourpenny answered 9/12, 2021 at 13:23 Comment(0)
H
5

You can do like this

<li *ngFor='let in of [0,1,2,3,4];let i = index'>{{i}}</li>
Hacking answered 18/10, 2017 at 8:33 Comment(2)
you don't need let i = index as your array already has the same values.Evered
<li *ngFor='let i of [0,1,2,3,4]>{{i}}</li> is simplerRowan
M
3

I know this question is 3 years old, and that it have been answered, but I had a similar need and I wasn't fully satisfied about how to solve it. So I created a dedicated structural directive to handle those cases.

I published ngx-range that lets you write something like this:

<ul>
  <li *ngxRange="let value from 0 to 20 by 5">
    Item #{{value}}
  </li>
</ul>

Of course, each values can be a variable, like

export class AppComponent {
  start = 0;
  end = 5;
  step = 1;
}
<ul>
  <li *ngxRange="let value from start to end by step">
    Item #{{value}}
  </li>
</ul>
Maddening answered 10/12, 2020 at 9:24 Comment(0)
T
1

Try this

<div class="col-md-3 col-sm-4 item-container" *ngFor="let item of items">
  <div class="item">
    <p class="rank">
      <img src="" class="img-responsive"/>{{item.rank}}
    </p>
  </div>
</div>
Tayib answered 18/10, 2017 at 8:8 Comment(0)
T
1

For your specific use-case you can use this code:

HTML:

<div *ngFor="let i of arr(item.rank)"> // arr is declared as Array in our .ts file
   <img src="" class="img-responsive"
</div>

.ts:

export class SomeComponent {
  arr = Array; // declaring arr as Array
}

the arr will take in your rank attribute value and create an Array of that length and then you can easily loop as many times as your value represents.

try this code hope it will be helpful.

Timm answered 26/12, 2017 at 12:48 Comment(0)
A
0

Simplest way is to define an Array in the .ts file

i = Array
count : number = 3

You can dynamically change the value in any of your functions

In the template, the loop can be included as follows

 <div *ngFor="let j of i(count)">
    // Loop content here
 <div>
Amuse answered 21/12, 2018 at 17:40 Comment(0)
C
0

countries:any;
  
  constructor() {}
  ngOnInit() {
    this.countries = [
      {'id':1, 'name':'India'},
      {'id':2, 'name':'Japan'}
    ];
    
    console.log(this.countries );
  }
<ul>
    @for (c of countries; track c.id) {
      <li>{{ c.id }} {{ c.name }}</li>
    } @empty {
      <span>Empty list of countries</span>
    }
  </ul>
Crucifixion answered 16/6 at 7:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.