How do I hide my table element when the component is empty? - html

The html is printing all the API data in the table. Products and Components
The table should not print the product when the components are empty. What is the best way to do this?
I'm using angular 8
products = {
"id": 1,
"name": "John",
"components": [
{
"id": 130,
"name": "Price",
}
]
"isSelected": false }
products = {
"id": 2,
"name": "name",
"components": [] }
<div class="card">
<div class="card-body">
<table class="table table-striped table-bordered hover">
<thead>
<tr>
<th width="1%" class="text-center"></th>
<th width="8%" class="text-center">Id</th>
<th width="8%" class="text-center">Name</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let product of products; index as i">
<tr>
<td>
<div>
<button class="btn btn-sm btn-default"></button>
</div>
</td>
<td>{{product.id}}</td>
<td>{{product.name}}</td>
</tr>
<tr *ngFor="let prodComp of product.components">
<td></td>
<td>{{prodComp.id}}</td>
<td>{{prodComp.name}}</td>
</tr>
</ng-container>
</tbody>
</table>
</div>
</div>

You could add another container inside your *ngFor using *ngIf to test for that condition:
<ng-container *ngFor="let product of products; index as i">
<ng-container *ngIf="product.components.length > 0">
<tr>
<td>
<div>
<button class="btn btn-sm btn-default"></button>
</div>
</td>
<td>{{product.id}}</td>
<td>{{product.name}}</td>
</tr>
<tr *ngFor="let prodComp of product.components">
<td></td>
<td>{{prodComp.id}}</td>
<td>{{prodComp.name}}</td>
</tr>
</ng-container>
</ng-container>

Related

Display data in HTML table with multi level grouping

I have to show some data in the HTML table by grouping using rowspan.
Below is the expected GUI
I have the JSON Data like below. JSON Data here
Angular Code
<table class="table table-fixed" border="1">
<thead>
<tr>
<th>Country</th>
<th>State</th>
<th>City</th>
<th>Street</th>
<th>Male</th>
<th>Female</th>
<th>Others</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let country of Countries">
<tr *ngFor="let item of [].constructor(country.NoOfStreets); let streetIdx = index">
<ng-container *ngFor="let state of country.States; let stateIdx = index">
<td [attr.rowspan]="state.NoOfStreets" style="width: 15%">
{{state.StateName}}
</td>
</ng-container>
<ng-container *ngFor="let state of country.States; let stateIdx = index">
<ng-container *ngFor="let city of state.Cities; let cityIdx = index">
<td [attr.rowspan]="city.NoOfStreets" style="width: 15%">{{city.CityName}}</td>
<ng-container *ngFor="let street of city.Streets; let streetIdx = index">
<td style="width: 15%">{{street.StreetName}}</td>
<td style="width: 15%">{{street.Male}}</td>
<td style="width: 15%">{{street.Female}}</td>
<td style="width: 15%">{{street.Others}}</td>
</ng-container>
</ng-container>
</ng-container>
</tr>
</ng-container>
</tbody>
</table>
I could not able to generate the expected UI. I get the different UI and not getting rendered properly. I tried this one for almost a week and nothing worked out.
The PLUNK version is https://next.plnkr.co/edit/5nYNZ86BiWDke3GE?open=lib%2Fapp.ts&deferRun=1
What you want is to flatten all streats into array, so that you cal loop over it. The flat code will be:
const concat = (x,y) => x.concat(y)
const flatMap = (f,xs) => xs.map(f).reduce(concat, [])
let states = flatMap(c => c.States.map(s => ({Country:c, State: s})), this.Countries);
let cities = flatMap(c => c.State.Cities.map(s => ({Country:c.Country, State:c.State, City: s})), states);
this.streets = flatMap(c => c.City.Streets.map(str => ({Country:c.Country, State:c.State, City: c.City, Street: str})), cities);
And then easily check if each Country, State and City is first in group like:
<tbody>
<tr *ngFor="let str in streets">
<td *ngIf="firstCountryInGroup(str)" [rowspan]="numberOfCountry(str)">
{{str.Country.CountryName}}
</td>
<td *ngIf="firstStateInGroup(str)" [rowspan]="numberOfStatse(str)">
{{str.State.CityName}}
</td>
<td *ngIf="firstCityInGroup(str)" [rowspan]="numberOfCities(str)">
{{str.City.CityName}}
</td>
<td>{{str.Street.Name}}<td>
<td>{{str.Street.Male}}<td>
<td>{{str.Street.Female}}<td>
<td>{{str.StreetOthers}}<td>
</tr>
</tbody>
We need to have separate columns where we run a loop based on child or sibling - you will get the idea from the comments in the code below also
relevant TS:
<table class="table table-fixed" border="1">
<thead>
<tr>
<th>Country</th>
<th>State</th>
<th>City</th>
<th>Street</th>
<th>Male</th>
<th>Female</th>
<th>Others</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let country of Countries; let i = index">
<tr>
<!-- column 1 -->
<td>{{country.CountryName}}</td>
<!-- column 2 -->
<td>
<ng-container *ngFor="let state of country.States">
<tr>
<td> {{state.StateName}} </td>
</tr>
</ng-container>
</td>
<!-- column 3 -->
<td>
<ng-container *ngFor="let state of country.States">
<tr>
<td>
<ng-container *ngFor="let city of state.Cities">
<tr>
<td> {{city.CityName}} </td>
</tr>
</ng-container>
</td>
</tr>
</ng-container>
</td>
<!-- column 4 -->
<td>
<ng-container *ngFor="let state of country.States">
<tr>
<td>
<ng-container *ngFor="let city of state.Cities">
<tr>
<td>
<ng-container *ngFor="let street of city.Streets">
<tr>
<td>
{{street.StreetName}}
</td>
</tr>
</ng-container>
</td>
</tr>
</ng-container>
</td>
</tr>
</ng-container>
</td>
<!-- column 5 -->
<td>
<ng-container *ngFor="let state of country.States">
<tr>
<td>
<ng-container *ngFor="let city of state.Cities">
<tr>
<td>
<ng-container *ngFor="let street of city.Streets">
<tr>
<td>
{{street.Male}}
</td>
</tr>
</ng-container>
</td>
</tr>
</ng-container>
</td>
</tr>
</ng-container>
</td>
<!-- column 6 -->
<td>
<ng-container *ngFor="let state of country.States">
<tr>
<td>
<ng-container *ngFor="let city of state.Cities">
<tr>
<td>
<ng-container *ngFor="let street of city.Streets">
<tr>
<td>
{{street.Female}}
</td>
</tr>
</ng-container>
</td>
</tr>
</ng-container>
</td>
</tr>
</ng-container>
</td>
<!-- column 7 -->
<td>
<ng-container *ngFor="let state of country.States">
<tr>
<td>
<ng-container *ngFor="let city of state.Cities">
<tr>
<td>
<ng-container *ngFor="let street of city.Streets">
<tr>
<td>
{{street.Others}}
</td>
</tr>
</ng-container>
</td>
</tr>
</ng-container>
</td>
</tr>
</ng-container>
</td>
</tr>
</ng-container>
</tbody>
</table>
working stackblitz here
With this solution
https://stackblitz.com/edit/angular-gbhcun?file=src/app/app.component.html
Add below style to meet exact output
table, th, td {
border: 1px solid black;
}

Convert table cell to editable input

I want to create a table with 2 columns: Name, Email. Everytime I press the edit button, I want to transform the td into editable inputs. The problem is that if I have more users and I press the edit button, all users will become editable, not just the selected one. How can I solve this problem?
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of usersList">
<td *ngIf="!editUser">{{ user.name }}</td>
<td *ngIf="editUser"><input class=" form-control" size="1" type="text" [(ngModel)]="user.name"></td>
<td *ngIf="!editUser">{{ user.email }}
<td *ngIf="editUser"><input class=" form-control" size="1" type="text" [(ngModel)]="user.email"></td>
<td *ngIf="!editUser">
<a class="action-btn" (click)="onEdit()">
<p class="material-icons pointer">edit</p>
</a>
</td>
</tr>
</tbody>
</table>
editUser: boolean = false
onEdit() {
this.editUser = !this.editUser
}
How the table looks before pressing the red button
How the table looks after pressing the button
Thank you for your time! (this is what I want to achieve
Do you have an id for the user?
Then you could do something like:
<tbody>
<tr *ngFor="let user of usersList">
<td *ngIf="editUserId !== user.id">{{ user.name }}</td>
<td *ngIf="editUserId === user.id"><input [(ngModel)]="user.name"></td>
<td *ngIf="editUserId !== user.id">{{ user.email }}
<td *ngIf="editUserId === user.id"><input [(ngModel)]="user.email"></td>
<td *ngIf="editUser !== user.id">
<a class="action-btn" (click)="onEdit(user.id)">
<p class="material-icons pointer">edit</p>
</a>
</td>
</tr>
</tbody>
and
editUserId: number;
onEdit(userId: number) {
this.editUserId = userId;
}
Try this, it will work for you.
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of usersList; index as i">
<td *ngIf="i!=selectedRowIndex">{{ user.name }}</td>
<td *ngIf="selectedRowIndex == i"><input class=" form-control" size="1"
type="text" [(ngModel)]="user.name"></td>
<td *ngIf="i!=selectedRowIndex">{{ user.email }}
<td *ngIf="selectedRowIndex == i"><input class=" form-control" size="1"
type="text" [(ngModel)]="user.email"></td>
<td>
<a class="action-btn" (click)="onEdit(i)">
<p class="material-icons pointer">edit</p>
</a>
</td>
</tr>
</tbody>
</table>
selectedRowIndex = -1
onEdit(rowIndex) {
this.selectedRowIndex = rowIndex;
}

How to show certain elements in <td> if info is empty

I am new in angular and I am using ngFor inside a to populate a table. My question is, if the array that is present[let i of user.acessTypes] in ngFor is empty how can I show an "Is Empty" string information in the correspondent row?
This is my table html
<table class="table table-bordered">
<tr>
<th>Email</th>
<th>Acess Type</th>
</tr>
<tr *ngFor="let user of listUser">
<td>{{user.email}}</td>
<td>
<button class="btn btn-primary"
*ngFor="let i of user.acessTypes">
//if i is empty show "Is Empty"
{{i.accessTypeName}}({{i.subAcessTypeName}})
</button> </td>
</tr>
</table>
This is my JSON response
{
"data": [
{
"id": 1,
"email": "jose#hotmail.com",
"password": "$2a$10$44ghfG4Ym4COxXbj9pDBuOLBXCPRRDiIM7y77G.XEh7avm2GOxlUC",
"isAdmin": 0,
"acessTypes": [
{
"id": 1,
"accessTypeName": "User",
"subAcessTypeName": "Ver&Escrever"
}
],
"tomas": [],
"consultas": [],
"profile": "NORMALUSER"
}
],
"dataArray": null,
"errors": []
}
There are several approaches. One of them is working with an if-else statement the Angular way. Both ng-container and ng-template won't be part of the DOM tree after they are rendered.
Here is a nice resource that explains it in more detail: https://toddmotto.com/angular-ngif-else-then
<table class="table table-bordered">
<tr>
<th>Email</th>
<th>Acess Type</th>
</tr>
<tr *ngFor="let user of listUser">
<td>{{user.email}}</td>
<td>
<ng-container *ngIf="user.acessTypes.length > 0; else noAccessTypes">
<button class="btn btn-primary" *ngFor="let i of user.acessTypes">
{{i.accessTypeName}}({{i.subAcessTypeName}})
</button>
</ng-container>
<ng-template #noAccessTypes>
<button class="btn btn-primary">Is empty</button>
</ng-template>
</td>
</tr>
</table>
<table class="table table-bordered">
<tr>
<th>Email</th>
<th>Acess Type</th>
</tr>
<tr *ngFor="let user of listUser">
<td>{{user.email}}</td>
<td>
<button class="btn btn-primary"
*ngFor="let i of user.accessTypes">
//if i is empty show "Is Empty"
{{i.accessTypeName}}({{i.subAcessTypeName}})
</button>
<button class="btn btn-primary" *ngIf="user.accessTypes.length == 0">Is Empty</button>
</td>
</tr>
</table>
You can just check using *ngIf as follows,
<button class="btn btn-primary" *ngFor="let i of user.acessTypes">
<ng-template *ngIf="i">
{{i.accessTypeName}}({{i.subAcessTypeName}})
</ng-template>
<ng-template *ngIf="!i">
Is Empty
</ng-template>
</button> </td>
Simply you can create a proper validation check as follows.
<table class="table table-bordered">
<tr>
<th>Email</th>
<th>Acess Type</th>
</tr>
<tr *ngFor="let user of listUser">
<td>{{user.email}}</td>
<td>
<button class="btn btn-primary"
*ngFor="let i of user.acessTypes">
<div *ngIf="i.accessTypeName.length == 0">
Is Empty
</div>
<div *ngIf="i.accessTypeName.length != 0">
{{i.accessTypeName}}({{i.subAcessTypeName}})
</div>
</button>
</td>
</tr>
</table>

How to get the header for each entry of json in AngularJS html file?

I have data values present in json file like below:
$scope.entries = {
"27": {
"e": {
"d": "v1",
"r": "v2"
}
},
"03": {
"e": {
"d": "v3 ",
"r": "v4"
}
}
}
I need to get the header value present for each entry (i.e 27 and 03) and display in one of columns for each row in my html table. I am trying through this but no result:
<table style="width:100%">
<tr>
<th>Code</th>
<th>Value</th>
<th> </th>
<th> </th>
<th> </th>
</tr>
<div class="row" ng-repeat="x in entries">
<tr ng-repeat="y in entries[x]">
<td>
<!--<need to display 27 here in first row then 03 in next row>-->
{{x}}
</td>
<td>
{{y.e.d+' : '+ y.e.r}}
</td>
<td>
<i class="fa fa-database"></i>View Details<!--more details hyperlink with router functionality-->
</td>
<td>
Edit
</td>
<td>
Delete
</td>
</tr>
</div>
</table>
Any idea on how to do that?
You can use ng-repeat with (key, value).
<tr ng-repeat="(key, value) in entries">
<td> {{key}} </td> <!-- Displays 27 in first row -->
...
</tr>

vuejs toogle a table row inside a table when checkbox check/uncheck

I am iterating a object and displaying the result on a table.
HTML:
<div id="app">
<div id="app">
<p> SeletedId's {{ selectedIds }}</p>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr class="text-center">
<th>
<span class="badge badge-info">Select</span>
</th>
<th>
<span class="badge badge-success">Name</span>
</th>
</tr>
</thead>
<tbody v-for="(pay,i) in PaymentType" :key="i">
<tr>
<td>
<input type="checkbox" v-model="selectedIds" :value="{selectedId: pay.Id}" />
</td>
<td>{{pay.Name}}</td>
</tr>
<!---I wanted to show the input box here -->
</tbody>
</table>
</div>
</div>
Vue:
new Vue({
el: '#app',
created(){},
data: {
message: 'Hello Vue.js!',
PaymentType: [{
Id: 1,
Name: "Cash",
HasOtherField: false,
Remarks: ""
},
{
Id: 2,
Name: "Check",
HasOtherField: false,
Remarks: ""
},
{
Id: 3,
Name: "Others",
HasOtherField: true,
Remarks: ""
}
],
selectedIds: []
}
})
I wanted to show a div or a row with a input box below the table. That should show/hide with the check box checked or unchecked.
For Hiding or showing the the input box i am implemented the following:
<div v-show="selectedIds[i]">
<span>Remarks</span><input type="text" class="form-control"/>
</div>
This is what i have done so far. The label Remarks and input box is not properly in-line.
I have manage to display the input box in-line by adding the following <tr></tr>
on the above code.
<tr v-show="selectedIds[i]">
<td colspan="2">
<span>Remarks</span>
<input type="text" class="form-control" />
</td>
</tr>
On My final template
<div id="app">
<div id="app">
<p> SeletedId's {{ selectedIds }}</p>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr class="text-center">
<th>
<span class="badge badge-info">Select</span>
</th>
<th>
<span class="badge badge-success">Name</span>
</th>
</tr>
</thead>
<tbody v-for="(pay,i) in PaymentType" :key="i">
<tr>
<td><input type="checkbox" v-model="selectedIds" :value="{selectedId: pay.Id}" /></td>
<td>{{pay.Name}}</td>
</tr>
<tr v-show="selectedIds[i]">
<td colspan="2">
<span>Remarks</span>
<input type="text" class="form-control" />
</td>
</tr>
</tbody>
</table>
</div>
</div>
Fiddle!
Use the template for the loop instead of the tbody, and v-if instead of v-show:
<template v-for="(pay, i) in PaymentType">
<tr :key="i*2">
<td>
<input type="checkbox" v-model="selectedIds" :value="pay.Id" />
</td>
<td>{{pay.Name}}</td>
</tr>
<tr :key="i*2 + 1" v-if="selectedIds.indexOf(pay.Id) !== -1">
<td> </td>
<td>
Remarks <input type="text" class="form-control" v-model="pay.Remarks" />
</td>
</tr>
</template>
[ https://jsfiddle.net/dcr202yn/ ]