ngStyle table based on arrays value - html

I want to obtain a table like this:
I have 2 objects
users = [
{ name: 'john', age: '22' },
{ name: 'mike', age: '20' },
{ name: 'dan', age: '12' },
{ name: 'anne', age: '16' },
{ name: 'jenny', age: '42' },
]
names = [
{ name: 'john', color: 'black' },
{ name: 'mike', color: 'black' },
{ name: 'dan', color: 'red' },
{ name: 'anne', color: 'red' },
{ name: 'jenny', color: 'red' },
]
If a name from names is in users, I want it's color to be black inside table, if not, I want it to be red.
This is my html:
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of users">
<ng-container *ngFor="let x of names">
<ng-container *ngIf="x.name == user.name">
<td [ngStyle]="{ 'color': names.color }">{{ user.name }}</td>
<td [ngStyle]="{ 'color': names.color }">{{ user.age }}</td>
</ng-container>
</ng-container>
</tr>
</tbody>
</table>
But it's not working properly. You can see a working snippet here
How can I achieve what I want? Thank you for your time!

You can either create a method that retrieves the color based on name (that you put in your component)
getColor(name) {
return this.names.find(e => e.name === name).color;
}
and call with
<td [style.color]="getColor(user.name)">{{ user.name }}</td>
<td [style.color]="getColor(user.name)">{{ user.age }}</td>
You don't need your double loop when using it like that, but it still needs to do a lookup loop for every iteration.
Better is to combine the two arrays before and use that
combined = [];
constructor() {
this.combined = this.users.map(e => Object.assign(e, this.names.find(x => x.name === e.name)))
}
usage
<tr *ngFor="let user of combined">
<td [style.color]="user.color">{{ user.name }}</td>
<td [style.color]="user.color">{{ user.age }}</td>
</tr>

You have a typo when referencing color. color is a property of the object, not the array you are looping through:
<td [ngStyle]="{ 'color': names.color }">{{ user.name }}</td>
<td [ngStyle]="{ 'color': names.color }">{{ user.age }}</td>
Should be
<td [ngStyle]="{ 'color': x.color }">{{ user.name }}</td>
<td [ngStyle]="{ 'color': x.color }">{{ user.age }}</td>

Related

How to display <td> value based on <th> value

I have three arrays which I have to display in the table. The three arrays are as follows:
let array1 = [
{id: 'p1', name: 'Adam', age: 27},
{id: 'p2', name: 'Jack', age: 20}
{id: 'p3', name: 'Jane', age: 21}
{id: 'p4', name: 'Rock', age: 23}
];
let array2 = [
{id: 'p3', occupation: 'Teacher', marital_status: 'Married'},
{id: 'p4', career: 'Wrestler', hobby: 'Gymming'}
];
let headers = [
{index: 1, name: 'id'},
{index: 2, name: 'name'},
{index: 3, name: 'age'},
{index: 4, name: 'occupation'},
{index: 5, name: 'marital_status'},
{index: 6, name: 'career'},
{index: 7, name: 'hobby'},
];
Now I want to display it inside the table. Here's what I've tried so far.
<table>
<thead>
<tr>
<th *ngFor="let head of headers">{{head?.name}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let person of array1; index as i">
<td>{{ person?.name ? person?.name : '-' }}</td>
<td>{{ person?.id }}</td>
<td>{{ person?.age }}</td>
<ng-container *ngFor="let header of headers">
<td *ngFor="let item of array2">
<ng-container *ngIf="header.name == item[header.name]">{{ item[header.name]}}</ng-container>
</td>
</ng-container>
</tr>
</tbody>
</table>
I want to display the data based on table header's (th) value
I think you're looking for a result like the one in this stackblitz. You needed to connect the ids between your arrays. The solution in this stackblitz is not a good one, however. You shouldn't put methods in your template as they will run an excessive amount due to change detection. I only created it this way to provide an example solution.
What I would recommend is combining array1 and array2 so that all of your elements have the extra fields in one object, instead of referencing two objects that share the same id. This is the simplest and most efficient solution. Alternatively, you could create a pipe that produces the correct result instead of using a method within the template.
create an unique array
//first create an objct with all the elements
const emptyObj=headers.reduce(a:any,b:any)=>({...a,b.name:''))
//then create an object using spread operator
arrayFull=this.array1.map(x=>{
const el=this.array2.find(y=>y.id==x.id)
return {...emptyObj,...x,...el}
})

Display multi arrays with rowspan in angular

Hi I am trying to create a table with rowspan based on array lengths. It works for me with 2 nested arrays but I have no idea ho to do it with more nest levels.
For data:
data = [
{ id: 1, name: 'one', groups:
[{'name': 'gr1', campaigns :
[{'name' : 'camp1'}, {'name' : 'camp2'}, {'name' : 'camp3'}]},
{'name': 'gr2', campaigns :
[{'name' : 'camp4'}, {'name' : 'camp5'}, {'name' : 'camp6'}]}
] },
{ id: 2, name: 'two', groups: [{'name': 'gr3', campaigns : [{'name' : 'camp7'}]}] },
{ id: 3, name: 'three', groups: [{'name': 'gr4', campaigns : [{'name' : 'camp8'}]}] },
{ id: 4, name: 'four', groups: [{'name': 'gr5', campaigns : [{'name' : 'camp9'}]}] }
];
I tried:
<table class="table table-bordered table-hover hidder" id="report">
<!-- Header -->
<tr>
<th >Project</th>
<th>Group</th>
<th>Camps</th>
</tr>
<ng-container *ngFor="let project of data; let i = index;">
<tr>
<td [attr.rowspanspan]="project.groups.length"> {{project.name}} </td>
<ng-container *ngFor="let group of project.groups; let j = index">
<tr>
<td>
{{ group.name }}
</td>
<!-- <ng-container *ngFor="let campaign of group.campaigns; let k = index">
<tr>
<td >
{{ campaign.name }}
</td>
</ng-container> -->
</tr>
</ng-container>
</ng-container>
</table>
My goal is to get table like this in picture:
image example
For exampleI have code here:
working code example
The nature of the table structure in HTML makes it quite difficult to split it programmatically. You could try the following, but then you'll have to style the table yourself:
<table class="table table-bordered table-hover hidder" id="report">
<!-- Header -->
<tr>
<th>Project</th>
<th>Group</th>
<th>Camps</th>
</tr>
<ng-container *ngFor="let project of data; let i = index">
<tr>
<td>{{ project.name }}</td>
<td>
<table>
<ng-container *ngFor="let group of project.groups; let j = index">
<tr click="test(group);">
<td>{{ group.name }}</td>
</tr>
</ng-container>
</table>
</td>
<td>
<table>
<ng-container *ngFor="let group of project.groups; let j = index">
<ng-container *ngFor="let campaign of group.campaigns; let k = index">
<tr click="test(group);">
<td>{{ campaign.name }}</td>
</tr>
</ng-container>
</ng-container>
</table>
</td>
</tr>
</ng-container>
</table>
Check out the StackBlitz example.
Should be [attr.rowspan], but I think you should remove it, see your forked stackblitz.
NOTE: I added in the .css to override the bootstrap padding of a table
.table > :not(caption) > * > *
{
padding:0 .5rem;
}
NOTE2: we use [attr.rowspan] if we have a "flat" array, like this another SO. not when we have an array with "groups"

How to prevent nz-select from expanding width inside ng-zorro table?

I use nz-select inside the ng-zorro table. Initially, I set the 25% as the width for all column in the table. However, when I select the color, as the name of the color is too long, it expands the color column and it is no longer width 25%.
How can I prevent the color column from expanding and remain as 25% width after I choose the color from the selection dropdown?
I also attached the html and ts file below.
Initially
After choose color
<nz-table #basicTable [nzData]="listOfData">
<thead>
<tr>
<th nzWidth="25%" >Name</th>
<th nzWidth="25%">Age</th>
<th nzWidth="25%">Color <br> (Choose 3)</th>
<th nzWidth="25%">Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of basicTable.data">
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>
<nz-select style="width: 100%" nzMode="multiple" nzPlaceHolder="Select Colors">
<nz-option *ngFor="let option of colorOption" [nzLabel]="option" [nzValue]="option">
</nz-option>
</nz-select>
</td>
<td>
<a>Delete</a>
</td>
</tr>
</tbody>
</nz-table>
listOfData: Person[] = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park'
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park'
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park'
}
];
colorOption = [
"Option Color Number 1",
"Option Color Number 2",
"Option Color Number 3",
"Option Color Number 4",
"Option Color Number 5",
"Option Color Number 6",
]
<nz-select style="width: 100%" nzMode="multiple" nzPlaceHolder="Select Colors" appSelectWidth>
<nz-option *ngFor="let option of colorOption" [nzLabel]="option" [nzValue]="option">
</nz-option>
</nz-select>
import {AfterViewInit, Directive, ElementRef, OnInit, Renderer2} from '#angular/core';
#Directive({
selector: '[appSelectWidth]'
})
export class SelectWidthDirective implements AfterViewInit {
constructor(private ElementRef: ElementRef, private Render2: Renderer2) {
}
ngAfterViewInit() {
setTimeout(() => {
const {width} = this.ElementRef.nativeElement.getBoundingClientRect();
console.log(width);
this.Render2.setStyle(this.ElementRef.nativeElement, 'width', width + 'px')
}, 200)
}
}
so easy to prevent nz-select from expanding width inside ng-zorro table
you must add nz-select width style in 'px'
like this
<nz-table #basicTable [nzData]="listOfData">
<thead>
<tr>
<th nzWidth="25%" >Name</th>
<th nzWidth="25%">Age</th>
<th nzWidth="25%">Color <br> (Choose 3)</th>
<th nzWidth="25%">Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of basicTable.data">
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>
<nz-select style="width: 100px" nzMode="multiple" nzPlaceHolder="Select Colors">
<nz-option *ngFor="let option of colorOption" [nzLabel]="option" [nzValue]="option">
</nz-option>
</nz-select>
</td>
<td>
<a>Delete</a>
</td>
</tr>
</tbody>
</nz-table>
check this

Angular populating table with object data that has missing fields

i'm currently working on displaying large amounts of data in tables for a scientific project. i have timeStamps of experiments, which look like this:
interface TimeData {
time: string;
data: {SD: string, SEM: string, N: string, MEAN: string};
}
i have a total of 11 timeStamps in the table, all described by their 'time', e.g. 15, 30, 60, 90...
if an experiment is missing a timestamp completely or has no data for the timestamp 30 at e.g. MEAN, i want to print -- in the td.
i can't seem to wrap my head around how to display all mean data in a row and just replacing missing one's with a '--'...
i've tried wrapping in ng-container, several ngFor loops, yet it always comes out wrong, i.e. i get too many -- or none at all and my data is being displayed in the wrong td.
here's the html
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">Code</th>
<th scope="col">Function</th>
<th scope="col" class="text-center"
*ngFor="let timeStamp of maxTimeStamps; let i=index">t{{i + 1}}
{{timeStamp.time}}</th>
<th>ΔAUC</th>
</tr>
</thead>
<tbody *ngFor="let experiment of report.experiments; let i = index">
<tr>
<td rowspan="4" [ngStyle]="{color: experimentColors[i]}">{{experiment.code.toUpperCase()}}</td>
<td>mean</td>
------missing td goes here-------
----------------------------------
</tr>
</tbody>
</table>
Welcome to StackOverflow, If I get it right you want to show your data in rows but you add ngFor in the wrong place for that (it shouldn't add to tbody tag)! please check the corrected code below.
You can change or make a condition by ngIf if you don't want to add that td.
Good Luck.
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">Code</th>
<th scope="col">Function</th>
<th scope="col" class="text-center"
*ngFor="let timeStamp of maxTimeStamps; let i=index">t{{i + 1}}
{{timeStamp.time}}</th>
<th>ΔAUC</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let experiment of report.experiments; let i = index">
<td rowspan="4" [ngStyle]="{color: experiment.Colors[i]}" [ngIf]="experiment.CheckableItem == true">{{experiment.code.toUpperCase()}}</td>
<td>mean</td>
</tr>
</tbody>
</table>
If some of the properties will not be available in the interface then they should be defined optional. Notice the question mark.
interface TimeData {
time?: string;
data?: {SD?: string, SEM?: string, N?: string, MEAN?: string};
}
In the template, you could use *ngIf directive with an else clause to show '--' if some properties are not available. Try the following
Controller
import { Component, VERSION, ElementRef } from '#angular/core';
import { Router } from '#angular/router';
interface TimeData {
time?: string;
data?: {SD?: string, SEM?: string, N?: string, MEAN?: string};
}
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
timeData: TimeData[] = [
{time: '15', data: {SD: '10', SEM: '20', N: '30', MEAN: '75'}},
{data: {SD: '10', SEM: '20', N: '30', MEAN: '75'}},
{time: '15', data: {SD: '10', SEM: '20', N: '30'}},
{data: {SEM: '20', N: '30', MEAN: '75'}},
];
constructor(private _router: Router, private _el: ElementRef) { }
}
Template
<table>
<tr>
<th>Time</th>
<th>SD</th>
<th>SEM</th>
<th>N</th>
<th>MEAN</th>
</tr>
<tr *ngFor="let data of timeData">
<ng-container *ngIf="data.time; else emptyData">
<td>{{ data.time }}</td>
</ng-container>
<ng-container *ngIf="data.data.SD; else emptyData">
<td>{{ data.data.SD }}</td>
</ng-container>
<ng-container *ngIf="data.data.SEM; else emptyData">
<td>{{ data.data.SEM }}</td>
</ng-container>
<ng-container *ngIf="data.data.N; else emptyData">
<td>{{ data.data.N }}</td>
</ng-container>
<ng-container *ngIf="data.data.MEAN; else emptyData">
<td>{{ data.data.MEAN }}</td>
</ng-container>
<ng-template #emptyData>
<td>--</td>
</ng-template>
</tr>
</table>
Working example: Stackblitz

Issue while printing in HTML

I am getting an array of users in response and there is one more array of roles in that respose and I want to print that role in my HTML.
Response :
User
email : "musaevt#yahoo.com"
firstName : "Timur"
fullName : "Timur Musaev"
id : "auth0|5a1ff162a916760a80b8b838"
lastName : "Musaev"
roles : Array(2)
0 : "Student"
1 : "Guest"
HTML :
<tbody>
<tr *ngFor="let user of users
| hipUsersSorter: key: direction
| paginate: { id: 'server', itemsPerPage: usersPerPage, currentPage: currentPage, total: totalUsers }">
<td>{{ user.lastName }}</td>
<td>{{ user.firstName }}</td>
<td>{{ user.email }}</td>
<td *ngFor ="let role of user.roles" >
{{ role | translate }}
</td>
<td>
<button md-icon-button color="primary" [routerLink]="[ '/users/edit-user', user.id ]">
<md-icon>edit</md-icon>
</button>
</td>
</tr>
</tbody>
I am able to print this but because of 2 values in roles array, it is creating a new column and alignment gets changed. How can i print those two roles without changing the alignment of my edit button?
What about not looping on the td, but rather create a looping span inside the td?
<td>
<span *ngFor ="let role of user.roles" >
{{ role | translate }}
</span>
</td>
Here's the full HTML code:
<tbody>
<tr *ngFor="let user of users
| hipUsersSorter: key: direction
| paginate: { id: 'server', itemsPerPage: usersPerPage, currentPage: currentPage, total: totalUsers }">
<td>{{ user.lastName }}</td>
<td>{{ user.firstName }}</td>
<td>{{ user.email }}</td>
<td>
<span *ngFor ="let role of user.roles" >
{{ role | translate }}
</span>
</td>
<td>
<button md-icon-button color="primary" [routerLink]="[ '/users/edit-user', user.id ]">
<md-icon>edit</md-icon>
</button>
</td>
</tr>