How to combine two arrays into one array of objects? - html

Can I use an ngFor instead of repeating <table> two times?
NB: I thought to combine all the items into objects as items of a single array of mapping(each object contains a variable, label and value) but it does not work for me)
....
this.maxValueTable.push(selectedData.res.maxValue);
this.minValueTable.push(selectedData.res.minValue);
...
<div style="display: flex;">
<table style="width:100%;">
<thead>
<tr>
<th>Max</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let maxValue of maxValueTable">
<td> {{ maxValue | numberFormatter: (getUnit() | async)}}</td>
</tr>
</tbody>
</table>
<table style="width:100%;">
<thead>
<tr>
<th>Min</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let maxValue of minValueTable">
<td> {{ MinValue| numberFormatter: (getUnit() | async)}}</td>
</tr>
</tbody>
</table>
</div>

Another way:
<!--create an array directly in the .html: you can also use
a variable in your .ts-->
<table *ngFor="let table of [{head:'Max',data:maxValueTable},
{head:'Max',data:maxValueTable}]
style="width:100%;">
<thead>
<tr>
<!--use table.head-->
<th>{{table.head}}</th>
</tr>
</thead>
<tbody>
<!--see how iterate over table.data-->
<tr *ngFor="let maxValue of table.data">
<td> {{ maxValue | numberFormatter: (getUnit() | async)}}</td>
</tr>
</tbody>
</table>
If your arrays has the same length and only want a table, iterate over one array ans use the index to get the value of the another one
<table style="width:100%;">
<thead>
<tr>
<th>Max</th>
<th>Min</th>
</tr>
</thead>
<tbody>
<!--see the "let i=index"-->
<tr *ngFor="let maxValue of maxValueTable;let i=index">
<td> {{ maxValue | numberFormatter: (getUnit() | async)}}</td>
<!--use the "index" "i" to get the element of the another array-->
<td>
{{ minValueTable[i] | numberFormatter: (getUnit() | async)}}
</td>
</tr>
</tbody>
</table>
in this case you can also use map to create a new Array
minMax=this.minValueTable.map((x,index)=>({
min:x,
max:this.maxValueTable[index]
}))
And use {{value.min}} and {{value.max}}

You can create a function that will combine min and max values into an array like that:
mergeIntoArray(maxValue: Array<number>, minValue: Array<number>): IMergedObj[] {
let mergedArray = [];
maxValue.forEach((value, index) => {
let tempObj = {};
tempObj['maxValue'] = value;
tempObj['minValue'] = minValue[index];
mergedArray.push(tempObj);
});
return mergedArray;
}
and call this function like that:
let minAndMax = [];
this.minAndMax = this.mergeIntoArray(this.maxValueTable, this.minValueTable);
after that, use this variable (minAndMax) in your HTML template. This way you do not need to use ngFor twice.
<table style="width:100%;">
<thead>
<tr>
<th>Max</th>
<th>Min</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of minAndMax">
<td>{{ item.maxValue }}</td>
<td>{{ item.minValue }}</td>
</tr>
</tbody>
</table>
Here is the stackblitz link created for you. Hope that might help you.

You can use *ngTemplateOutlet for this case:
<div style="display: flex;">
<ng-container *ngTemplateOutlet="tableTemplate; context: {$implicit: maxValueTable, header: 'Max'}"></ng-container>
<ng-container *ngTemplateOutlet="tableTemplate; context: {$implicit: minValueTable, header: 'Min'}"></ng-container>
</div>
<ng-template #tableTemplate let-values, let-header="header">
<table style="width:100%;">
<thead>
<tr>
<th>{{ header }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let value of values">
<td> {{ value | numberFormatter: (getUnit() | async) }}</td>
</tr>
</tbody>
</table>
</ng-template>
And you will get the same table rendered twice - for Max and Min values.
As you can see, the arguments are passed as a second argument inside ngTemplateOutlet:
context: {$implicit: valuesArgument, header: headerArgument}
Later you could use this template to create infinite amount of tables:
<div *ngFor="let table of tables">
<ng-container *ngTemplateOutlet="tableTemplate; context: {$implicit: table.values, header: table.header}"></ng-container>
</div>
Assuming your tables property will look like
export class C {
tables: Table[] = [{header: 'Min', values: [1, 2, 3]}, {header: 'Max', values: [4, 5, 6]}, {header: 'Other', values: [0, -1, -2]}]
}

Related

Calling function from template: How to assign the value to variable and subscribe to it a change?

The template is calling the getMaxUnit() function and this renders time. How can assign the value to a variable and subscribe to it a change using setting service observables? I am not able to found a solution for that
public getMaxUnit():Observable<string>{
return of(MaxUnits.Dollars);
}
<div style="display: flex;">
<table style="width:100%;">
<thead>
<tr>
<th>Max</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let maxValue of maxValueTable">
<td> {{ maxValue | numberFormatter: (getMaxUnit() | async)}}</td>
</tr>
</tbody>
</table>
</div>

How to call a function from an HTML template using observables?

I used to call functions from the template but I learned recently that it is not a good idea since it renders time. I tried to assign value to variable and subscribe it to change using setting service observables but it does not work for me.
public getMaxUnit(){
return MaxUnits.Dollars;
}
<div style="display: flex;">
<table style="width:100%;">
<thead>
<tr>
<th>Max</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let maxValue of maxValueTable">
<td> {{ maxValue | numberFormatter: getMaxUnit()}}</td>
</tr>
</tbody>
</table>
</div>
How can I assign please the value to a variable using .subscribe() method?
You can use observables in your html page with async pipe.
In your code, your pipe takes an observable parameter.
To achieve this you need to wrap your getMaxUnit with async pipe.
Here is a similar example you can check and you can change getDateFormat function to how its date is changing.
public getMaxUnit():Observable<string>{
return of(MaxUnits.Dollars);
}
<div style="display: flex;">
<table style="width:100%;">
<thead>
<tr>
<th>Max</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let maxValue of maxValueTable">
<td> {{ maxValue | numberFormatter: (getMaxUnit() | async)}}</td>
</tr>
</tbody>
</table>
</div>

How to display object of array data on html table in Angular

I need to display below object of array in html table like that image. My data array like this.
data = [{id: 0 , name: 'one' , tags: ['a' , 'b' , 'c']} , {id: 1 , name: 'two', tags: ['r' , 't' , 'y']} , {id: 2 , name: 'three' , tags: ['a' , 'b' , 'c']} , {id: 3 , name: 'four' , tags: ['a' , 'b' , 'c']}]; .
So i tried like this but it's not working as expect on image.
<table class="table table-hover table-bordered">
<thead>
<tr>
<th scope="col">Tag Type</th>
<th scope="col">Tags</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let tag of data; let i = index">
<td>{{ tag.id }}</td>
<table class="table">
<tr *ngFor="let el of tag.tags">
<td>{{el}}</td>
</tr>
</table>
<td>
<div align="center">
<a matTooltip="edit tag"><i>mode_edit</i></a>
</div>
</td>
</tr>
</tbody>
</table>
this is the result i got.Any idea how to do this?
I have made a few modifications into the code.
Instead of creating a new nested table,I have tried create separate rows in the main table. I have hid the TagType And Action, where it is not to be displayed based on the index.
For the striped pattern, I have added "table-striped" class to the table.
The Html code would look like this
<table class="table table-hover table-bordered table-striped">
<thead>
<tr>
<th scope="col">Tag Type</th>
<th scope="col">Tags</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let tag of data; let i = index">
<tr *ngFor="let el of tag.tags; let i2=index">
<td *ngIf="i2==0"> {{tag.id}} </td>
<td *ngIf="i2!=0"> </td>
<td> {{el}} </td>
<td>
<div align="center">
<a matTooltip="edit tag"><i>mode_edit</i></a>
</div>
</td>
</tr>
</ng-container>
</tbody>
</table>
StackBlitz working demo: https://stackblitz.com/edit/angular-ivy-aqmrzy

How to iterate through an array of objects in angular

I have an array booksArr of objects of class book.
book.ts class
export class Book{
bookId:number;
bookName:string;
cost:number;
quantity:number;
constructor(bookId, bookType, cost, quantity){
this.cost=cost;
this.bookId=bookId;
this.bookName=bookName;
this.quantity=quantity;
}}
booksArr in books-list.component.ts
booksArr: book[] = [
new book(100, "The Alchemist", 20, 1),
new book(101, "Rich Dad Poor Dad", 50, 2),
new book(102, "Charolett's Web", 10, 1),
new book(103, "Harry Potter", 70, 4),
new book(104, "Gone Girl", 150, 3),
];
I want to create a table in html to display the details of these books.
books-list.component.html
<table border="1" *ngIf="bName">
<thead>
<tr>
<th>book ID</th>
<th>book Name</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let b of booksArr">
<td *ngIf="b.//WHAT SHOULD I PUT HERE"</td>
</tr>
</tbody>
</table>
You are already iterating through the booksArr.just need to use interpolation to display the data. Try like this
<table border="1" *ngIf="bName">
<thead>
<tr>
<th>book ID</th>
<th>book Name</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let b of booksArr">
<td>{{b.bookId}}</td>
<td>{{b.bookName}}</td>
</tr>
</tbody>
</table>
You are already iterating the array of books with *ngFor="let b of booksArr".
You want to now interpolate the values from each book in the array. When you are inside the array, you have access to the loop variable you declare in *ngFor. In this case the loop variable is b. You can now bind to the properties on b using curly brace interpolation. {{b.bookId}}.
<table border="1" *ngIf="bName">
<thead>
<tr>
<th>book ID</th>
<th>book Name</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let b of booksArr">
<td>{{b.bookId}}</td>
<td>{{b.bookName}}</td>
</tr>
</tbody>
</table>
Try:
<td> {{ b.bookId }} </td>
<td> {{ b.bookName }} </td>
No *ngIf is required in this case. Just try the following
<tbody>
<tr *ngFor="let b of booksArr">
<td>{{b.bookId}}</td>
<td>{{b.bookName}}</td>
</tr>
</tbody>
And in your .ts file you should either change
constructor(bookId, bookName, cost, quantity)
Or
this.bookName=bookType;
inside your constructor

Dynamic rowspan in angular

Number of persons and their mobiles numbers are dynamic. I need to show this in table.
Data can contain any number of pname and mobile numbers.
dataList = [
{
pname: "abc",
numbers: [{mobile1: 123, mobile2: 234}]
},
{
pname: "mno",
numbers: [{mobile1: 125, mobile2: 237}]
}
]
Template
<tr *ngFor="let data of dataList">
<td [attr.rowspan]="data.numbers.lenght">data.pname</td>
<td>data.numbers</td> // Here how do I show all mobile numbers of the person.
</tr>
Expected output is of below code.
table, th, td {
border: 1px solid black;
}
<table>
<tr>
<th>Pname</th>
<th>Numbers</th>
</tr>
<tr>
<td rowspan="2">Abc</td>
<td>123</td>
</tr>
<tr>
<td>234</td>
</tr>
<tr>
<td rowspan="2">Mno</td>
<td>125</td>
</tr>
<tr>
<td>237</td>
</tr>
</table>
Problem I am facing is I need to add tr after each run of *ngFor and the number of tr will depend on length of numbers array to make row span.
You can do it like shown below
<table>
<ng-container *ngFor="let data of dataList">
<tr>
<td [attr.rowspan]="data.numbers.length">{{data.pname}}</td>
<td>{{data.numbers[0]}}</td>
</tr>
<ng-container *ngFor="let number of data.numbers; let i= index;">
<tr *ngIf="i!=0">
<td>{{number}}</td>
</tr>
</ng-container>
</ng-container>
</table>
But datalist must be of the following format i.e. numbers must be an array
dataList = [
{
pname: "abc",
numbers: [123, 234]
},
{
pname: "mno",
numbers: [125, 237]
}
]
Simple.
You don't need index and nested ng-container.
<table>
<ng-container *ngFor="let data of dataList">
<tr>
<td [attr.rowspan]="data.numbers.length + 1">{{data.pname}}</td>
</tr>
<tr *ngFor="let number of data.numbers;">
<td>{{number}}</td>
</tr>
</ng-container>
</table>
Working example.
https://stackblitz.com/edit/angular-yrose8?file=src%2Fapp%2Fapp.component.html
it is simple to use two *ngFor and avoid repeat Code by data.numbers[0].
<table>
<ng-container *ngFor="let data of dataList">
<ng-container *ngFor="let number of data.numbers; let i= index;">
<tr>
<td *ngIf="i==0" [attr.rowspan]="data.numbers.length">{{data.pname}}</td>
<td>{{number}}</td>
</tr>
</ng-container>
</ng-container>
</table>