Why does 'as' work when '=' does not in ngFor? - html

<tr *ngFor = "let item of list; let i = index;">
The above code gives error:
Type 'number' is not assignable to type 'string'. td [ngModelGroup]="j" #temp="ngModelGroup"
But the below code runs fine
<tr *ngFor = "let item of list; let i as index;">
What is the difference in using i = index and i as index.Previously I was using i = index and it was working fine.
Can someone help me with this?

You forgot the let before the i...
That happens because Angular is searching for the i variable in your component logic (the .ts file). And it doesn't find it, so by adding let behind the variable, Angular knows that this is not a variable in the component logic but inside the view that should be created.
Here you can find more information about that:
https://angular.io/api/common/NgForOf#local-variables

You lack of the double quote sign.
Try this with 3 case below to better understand:
<tr *ngFor="let item of list; let i = index">
<td>{{i}}/{{item}}</td>
</tr>
<p>In this case variable i is a index of list, begin from 0.</p>
<hr>
<tr *ngFor="let item of list1; let j as index">
<td>{{j}}/{{item}}</td>
</tr>
<p>In this case variable j is a value of index in list.</p>
<hr>
<tr *ngFor="let item of list1; index as k">
<td>{{k}}/{{item}}</td>
</tr>
<p>In this case variable k is a index of list.</p>
Demo: https://stackblitz.com/edit/angular-ivy-zldcpt?file=src%2Fapp%2Fapp.component.html

let i = index is not proper Angular syntax anymore. Starting with Angular 5 (IIRC) it became let i as index. Did you upgrade Angular recently?
You can check the documentation to verify: https://angular.io/api/common/NgForOf

Related

Loop using *ngFor in angular from the last element to first element [duplicate]

Using Angular 2, I want to duplicate a line in a template multiple times. Iterating over an object is easy, *ngFor="let object of objects". However, I want to run a simple for loop, not a foreach loop. Something like (pseudo-code):
{for i = 0; i < 5; i++}
<li>Something</li>
{endfor}
How would I do this?
You could dynamically generate an array of however time you wanted to render <li>Something</li>, and then do ngFor over that collection. Also you could take use of index of current element too.
Markup
<ul>
<li *ngFor="let item of createRange(5); let currentElementIndex=index+1">
{{currentElementIndex}} Something
</li>
</ul>
Code
createRange(number){
// return new Array(number);
return new Array(number).fill(0)
.map((n, index) => index + 1);
}
Demo Here
Under the hood angular de-sugared this *ngFor syntax to ng-template version.
<ul>
<ng-template ngFor let-item [ngForOf]="createRange(5)" let-currentElementIndex="(index + 1)" [ngForTrackBy]="trackByFn">
{{currentElementIndex}} Something
</ng-template>
</ul>
You can instantiate an empty array with a given number of entries if you pass an int to the Array constructor and then iterate over it via ngFor.
In your component code :
export class ForLoop {
fakeArray = new Array(12);
}
In your template :
<ul>
<li *ngFor="let a of fakeArray; let index = index">Something {{ index }}</li>
</ul>
The index properties give you the iteration number.
Live version
Depending on the length of the wanted loop, maybe even a more "template-driven" solution:
<ul>
<li *ngFor="let index of [0,1,2,3,4,5]">
{{ index }}
</li>
</ul>
You can do both in one if you use index
<div *ngFor="let item of items; let myIndex = index>
{{myIndex}}
</div>
With this you can get the best of both worlds.
The better way to do this is creating a fake array in component:
In Component:
fakeArray = new Array(12);
InTemplate:
<ng-container *ngFor = "let n of fakeArray">
MyCONTENT
</ng-container>
Plunkr here
you can use _.range([optional] start, end). It creates a new Minified list containing an interval of numbers from start (inclusive) until the end (exclusive). Here I am using lodash.js ._range() method.
Example:
CODE
var dayOfMonth = _.range(1,32); // It creates a new list from 1 to 31.
//HTML Now, you can use it in For loop
<div *ngFor="let day of dayOfMonth">{{day}}</div>
The best answer for this question I have found here
You need to create an attribute inside your class and reference it to Array object:
export class SomeComponent {
Arr = Array; //Array type captured in a variable
num:number = 20;
}
And inside your HTML you can use:
<ul id="next-pages">
<li class="line" *ngFor="let _ of Arr(10)"> </li>
</ul>
queNumMin = 23;
queNumMax= 26;
result = 0;
for (let index = this.queNumMin; index <= this.queNumMax; index++) {
this.result = index
console.log( this.result);
}
Range min and max number
for Example let say you have an array called myArray if you want to iterate over it
<ul>
<li *ngFor="let array of myArray; let i = index">{{i}} {{array}}</li>
</ul>
If you want to use the object of ith term and input it to another component in each iteration then:
<table class="table table-striped table-hover">
<tr>
<th> Blogs </th>
</tr>
<tr *ngFor="let blogEl of blogs">
<app-blog-item [blog]="blogEl"> </app-blog-item>
</tr>
</table>
If you want duplicate lines multiple time.
You can simply do :-
declare in .ts file
public repeater = "<li>Something</li>";
Then use following to print it .html file.
{{repeater.repeat(5)}}

Angular : how to create the row dynamically in angular

I am using
<div *ngFor="let item of object | keyvalue">
{{item.key}}:{{item.value}}
</div>
to print the value in html. but I need to print it in form of table
say total we have 8 key value pair, now i want to create the new row after 4th key value pair, such that it should come in this way
a a a a
a a a a
can we do it dynamically, the key value pair can be 12 or 16, multiple of 4
you could convert your structure into a more suitable one and use a nested loop.
This would also lead to a data structure more similar to a table.
something like that
<table>
<tr *ngFor="let item of arr">
<td *ngFor="let td of item">
{{td}}
</td>
</tr>
</table>
and in your components ts file.
let i = 0;
const itemsPerRow = 4;
Object.keys(this.object).forEach((key,index) => {
if(index > 0 && index % itemsPerRow == 0) {
i++;
}
if(!this.arr[i]) {
this.arr[i] = [];
}
this.arr[i].push(this.object[key]);
});
this will convert your structure into an array of objects where every array item represents one row.
Here's a working stackblitz
Just as further info.
You can get the index of your current element in your *ngFor like
*ngFor="let item of items;let i = index"

How to assign a starting index value while using ngFor in angular

I have a requirement to always display minimum of 5 rows(5 or more rows) in a table. For example, if there are 2 rows available in Database, I need to display other 3 more rows in UI with empty rows.
Here is what I tried so far:
<div *ngFor="let task of tasks; let i = index">
<div class="rowDiv">{{task.id}}</div>
</div>
Here I want to run the loop from i = tasks.size to i < = 5. So that I have total of 5 rows in UI. How to achieve this?
<div *ngFor=" let i = index">
<div class="rowDiv"></div>
</div>
You can loop over an array of 5 items, and use *ngIf to display an additional row if no data item exists at a given index:
<div *ngFor="let task of tasks">
<div class="rowDiv">{{task.id}}</div>
</div>
<ng-container *ngFor="let i of [0,1,2,3,4]">
<div *ngIf="!tasks[i]">
<div class="rowDiv">This row is empty</div>
</div>
</ng-container>
See this stackblitz for a demo.
you can also add so many rows you need after
<table>
<row *ngFor="let task in task">
</row>
<!--if task.length<5-->
<ng-container *ngIf="tasks.length<5">
<!-use slice:0:5-tasks.length-->
<row *ngFor="let i of [0,1,2,3,4] |slice:0:5-tasks.length">
</row>
</ng-container>
</table>
You don't need to keep this logic in html.
In you class you can do something like this: (suppose you fetch tasks from server)
this.getTasks().subscribe((tasks) => {
const emptyTasks = Array(5).fill({id: 'empty'});
this.tasks = tasks.map((t, index) => t || emptyTasks[index]);
})
This could be better handled in the controller. In case of default change detection strategy, the template is reloaded multiple times without our control or knowledge. So it's better to make sure the tasks variable has atleast 5 elements in the controller rather than to control the flow in the template. You could something like the following in the controller and leave the template unchanged.
for (let i = 0; i < 5; i++) {
if(!this.tasks[i].id) {
this.tasks[i].id = '';
}
}

Is there a way to use iteration inside an object in html in angular?

Here's how my html code is looking:
<div *ngFor="let historyArticle of historyArticles; let i=index">
<div [innerHTML]='historyArticle[i].fields.text | mdToHtml'></div>
</div>
I want to target the every object inside the historyArticle array. Writing {{i}} inside a div gives me the index number for each entry but I want to use that to target the correct text field in each entry
You don't need the i index at all in this case. historyArticle itself is the object you want:
<div *ngFor="let historyArticle of historyArticles">
<div [innerHTML]='historyArticle.fields.text | mdToHtml'></div>
</div>
Using *ngFor in Angular is basically looping through an array.
So doing :
for (let i = 0; i < this.historyArticles.length; i++) {
// do something e.g console.log(this.historyArticles[i].fields.text);
}
Is pretty much the same as :
<div *ngFor="let historyArticle of historyArticles">
<span>{{ historyArticle.fields.text }}</span>
</div>
Hope it helps you understand that in this case you don't need to use i = index
try this.
<div *ngFor="let historyArticle of historyArticles; let i=index">
<div>{{historyArticle[i].fields.text | mdToHtml}}</div>
</div>

ordered List index not updated correctly

I have an issue with the ngIf in Angular. So basically I want to implement a notification system to notify the user of what fields are missing.
Here is a stackblitz for the issue: https://stackblitz.com/edit/angular-behnqj
To reproduce:
1. first select the vehicle type 'plane', here everything is correct, the first error is removed and the second remains with index = 1.
2. Now refresh the stackblitz preview webpage and this time select the vehicle type 'car', we can now see that the first error is removed but the second remains with index = 2.
With some debugging I figgured out if I swap between conditions in the ngIf and do something like
<ng-container *ngIf="selectedVehicleType === 'plane'; else carsOptions">
<option *ngFor="let plane of planes; let i=index" [value]="plane" [selected]="plane === selectedVehicle" [hidden]="i===0"> {{plane}}</option>
</ng-container>
</select>
<ng-template #carsOptions>
<option *ngFor="let car of cars; let i=index" [value]="car" [selected]="car === selectedVehicle" [hidden]="i===0" > {{car}}</option>
</ng-template>
This will result to the same issue but this time with the issue occuring when we select 'plane' and not 'car'.
change it to this it will work :)
<div class="content">
<span>errors:</span>
<ul style="list-style: none;">
<li *ngFor="let error of errors; let i=index;"> {{(i+1)+ ". "+error}}</li>
</ul>
</div>
hope it helps.