How to print table rows with table rows children in Angular? - javascript

I am looking for a way to print data received from API in a table working with Angular. The table have different collapses. The collapses have several rows which at the same time these rows have children rows and some of them have more children rows.
this is the json file:
{
"collapse1": [
{
"name": "Soil",
"budget": 12345,
"child": [
{
"name": "Shopping",
"budget": 12345
},
{
"name": "Financial",
"budget": 12345
}
]
},
{
"name": "Exterior",
"budget": 12345,
"child": [
{
"name": "Shopping",
"budget": 12345
},
{
"name": "Financial",
"budget": 12345
}
]
},
{
"name": "Obra",
"budget": 12345,
"child": [
{
"name": "Shopping",
"budget": 12345
},
{
"name": "Financial",
"budget": 12345
}
]
},
{
"name": "Professional honoraries",
"budget": 12345,
"child": [
{
"name": "Shopping",
"budget": 12345
},
{
"name": "Financial",
"budget": 12345
}
]
}
],
"collapse2":[
{
"name": "Soil",
"budget": 12345,
"child": [
{
"name": "Shopping",
"budget": 12345
},
{
"name": "Financial",
"budget": 12345
}
]
},
{
"name": "Construction",
"budget": 12345,
"child": [
{
"name": "Data Center",
"budget": 12345,
"child": [
{
"name": "Development & Design",
"budget": 12345,
"child": [
{
"name": "Raw Land",
"budget": 12345
},
{
"name": "Land Development",
"budget": 12345
},
{
"name": "Fiber Connection Upgrade",
"budget": 12345
}
]
}
]
}
]
}
]
}
I'm looking for a solution to dynamically print a table with data like in the json file. I tried to do the following just for the first collapse, but it did not work.
<ng-container *ngFor="let collapse of collapse1">
<tr class="group2 collapse" [collapse]="isCollapsedGroup2">
<td>{{ collapse.name }}</td>
<td></td>
<td></td>
</tr>
<ng-container *ngFor="let child of collapse1.child">
<tr class="tablerow-sub collapse" [collapse]="isCollapsedGroup2">
<td>
<em class="pl-3">{{ child.name }}</em>
</td>
<td></td>
<td></td>
</tr>
</ng-container>
</ng-container>

You can use ng-template to have a recursive loop in the template like this
https://stackblitz.com/edit/angular-playground-excmau?file=app%2Fapp.component.html
<ng-template #row let-data="collapse">
<tr>
<td>{{ data.name }}</td>
<td>{{ data.budget }}</td>
</tr>
<ng-container *ngIf="data.child">
<ng-container *ngFor="let child of data.child" [ngTemplateOutlet]="row"
[ngTemplateOutletContext]="{ collapse: child }"></ng-container>
</ng-container>
</ng-template>
<table>
<ng-container *ngFor="let c of collapse">
<ng-container [ngTemplateOutlet]="row" [ngTemplateOutletContext]="{ collapse: c }"></ng-container>
</ng-container>
</table>

Related

Loop through Javascript object with children in Angular template

I have the following js object:
{
"id": "1554038371930_ajhnms9ft",
"name": "CPS",
"nodes": [
{
"id": "1554038905938_le34li2cg",
"name": "Consumer Journey",
"nodes": [
{
"id": "1554039157958_kwab8rj5f",
"name": "Average Rating",
"nodes": []
},
{
"id": "1554039174126_p47ugwkbv",
"name": "Product Quality",
"nodes": [
{
"id": "1554039298091_ewdyefkql",
"name": "Performance",
"nodes": []
},
{
"id": "1554039306834_qf54k1dqe",
"name": "Reliability",
"nodes": []
},
{
"id": "1554039320002_vfkenjmct",
"name": "Comfort",
"nodes": []
}
]
},
{
"id": "1554039197951_ajvv8587d",
"name": "Supply & Delivery",
"nodes": []
},
{
"id": "1554735679177_g5tini7ga",
"name": "Behind Product",
"nodes": [
{
"id": "1554736595466_nt4owp9in",
"name": "Influencers",
"nodes": []
},
{
"id": "1554736608593_58yomqpya",
"name": "Brand Confidence",
"nodes": []
}
]
},
{
"id": "1554736413715_jhro1oh0r",
"name": "Economical Value",
"nodes": [
{
"id": "1554736664421_wng97pbz8",
"name": {
"en": "Price"
},
"nodes": []
},
{
"id": "1554736676408_d4kiy2wv8",
"name": "Promotion & Reward",
"nodes": []
}
]
}
]
}
]
}
I want to loop through it in my angular HTML template so I can have the following list:
CPS
Consumer Journey
Average Rating
Product Quality
Performance
Reliability
Comfort
Supply & Delivery
Behind Product
Influencers
Brand Confidence
Economical Value
Price
Promotion & Reward
PS: I have an unknown number of levels!
I tried to implement that but my solution works only if I have a known number of levels:
<div *ngFor="let item of data">
<p>{{ item.name }}</p>
<div *ngIf="item.nodes.length">
<div *ngFor="let subItem of item.nodes">
<p>{{ subItem.name }}</p>
<div *ngIf="subItem.nodes.length">
<div *ngFor="let child of subItem.nodes">
{{ child.name }}
</div>
</div>
</div>
</div>
</div>
you need a recursive component to loop through n levels:
#Component({
selector: 'recursive-list',
template: `
<div *ngFor="let item of data">
<p>{{ item.name }}</p>
<recursive-list *ngIf="item.nodes.length" [data]="item.nodes"></recursive-list>
</div>
`
})
export class RecursiveListComponent {
#Input() data;
}

Move an element at the beginning of an array of objects

I have array format like this
response = {
"data": [{
"districts": [{
"id": 1,
"name": "sikkim district",
"statistics": [{
"food saftey": 2,
"food ": 2,
"air pollution": 0
}]
}]
},
{
"districts": [{
"id": 2,
"name": "Bhojpur",
"statistics": [{
"food saftey": 1,
"food ": 1,
"air pollution": 1
}]
}]
}
],
}
and the required format is
{
"data": [
{
"district": "sikkim district",
"food saftey": 2,
"food ": 2,
"air pollution": 0
},
{
"district": "Bhojpur",
"food saftey": 1,
"food ": 1,
"air pollution": 1
},
],
}
The array format is in dynamic which keeps changing except the district
and the district has to be at the beginning of the array.
What you can do is put the property you know first in a column array and then get the other properties and loop over using the order in the column array.
Something like this:
Stackblitz
import {
Component
} from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
data = [{
"Bal Vivah": 1,
"Type": 0,
"districts": "East District"
},
{
"Bal Vivah": 1,
"Type": 0,
"districts": "West District"
},
{
"Bal Vivah": 1,
"Type": 0,
"districts": "North District"
}
]
columns: string[] = ["districts"];
constructor() {
// get the columns from the data
if (this.data) {
var dataObject = this.data[0];
for (var property in dataObject) {
if (property != "districts" && dataObject.hasOwnProperty(property)) {
this.columns.push(property);
}
}
}
}
}
<table>
<thead>
<tr *ngFor="let column of columns">
<th>{{column}}</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let columnData of data">
<tr *ngFor="let column of columns">
<td>
{{columnData[column]| json}}
</td>
</tr>
</ng-container>
</tbody>
</table>
Note: I changed your data to be valid json.

Binding Object to Vue multi-select

I have a table with two columns(Location and Users). The location is static information but the users is a multi-select using Vue-Select.
I need to shows users currently selected for a location on page load. I grab that information from a database.
I also need to be able to change the selected users of a location by using a multi-select that shows all users. Example Mock
Vue Outline
<table>
<thead>
<tr>
<th class="col-xs-2">Locations</th>
<th class="col-xs-8">Users</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows" v-bind:key="index">
<td>
<span>{{ row.location }}</span>
</td>
<td>
<v-select multiple v-model="row.users">
<option v-for="user in allUsers" :key="user.id" :value="user.id">{{ user.name }}</option>
</v-select>
</td>
</tr>
</tbody>
</table>
Vue
var app = new Vue({
el: '#el',
data() {
return {
errors: [],
loading: false,
rows: this.assignments,
allUsers: this.users
}
},
props: {
assignments: Array,
users: Array
},
})
Example of how rows are returned from database
[{
"locations":[
{ "id":1,
"name":"SomePlace, CA",
"users": [
{"id":1, "name":"Person One"},
{"id":2, "name":"Person Two"}
]
},
{ "id":2,
"name":"AnotherPlace, CA",
"users": [
{"id":3, "name":"Person Three"}
]
}
]
},
{
"locations":[
{ "id":1,
"name":"SomePlace, CA",
"users": [
{"id":1, "name":"Person One"},
{"id":2, "name":"Person Two"}
]
},
{ "id":2,
"name":"AnotherPlace, CA",
"users": [
{"id":3, "name":"Person Three"}
]
}
]
}]
Example of how all users are returned from database
[
["id":1, "name":"Person One"],
["id":2, "name":"Person Two"],
["id":3,"name":"Person Three"],
]
I had moved the data coming via props directly to data object, since your rows property has one item which contains locations array, i looped through the first item rows[0] and i put row as the select options :options="row" and for the second column i looped through the user of the selectedLocation :
Vue.component('v-select', VueSelect.VueSelect)
var app = new Vue({
el: '#app',
data() {
return {
errors: [],
loading: false,
rows: [{
"locations": [{
"id": 1,
"name": "SomePlace, CA",
"users": [{
"id": 1,
"name": "Person One"
},
{
"id": 2,
"name": "Person Two"
}
]
},
{
"id": 2,
"name": "AnotherPlace, CA",
"users": [{
"id": 3,
"name": "Person Three"
}]
}
]
},
{
"locations": [{
"id": 1,
"name": "SomePlace, CA",
"users": [{
"id": 1,
"name": "Person One"
},
{
"id": 2,
"name": "Person Two"
}
]
},
{
"id": 2,
"name": "AnotherPlace, CA",
"users": [{
"id": 3,
"name": "Person Three"
}]
}
]
}
],
allUsers: this.users
}
},
props: {
assignments: Array,
users: Array
},
})
<table>
<thead>
<tr>
<th class="col-xs-2">Locations</th>
<th class="col-xs-8">Users</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows[0].locations" v-bind:key="index">
<td class="lead-locations">
{{row.name}}
</td>
<td class="lead-users">
<v-select multiple v-model="row.users" label="name">
</v-select>
</td>
</tr>
</tbody>
</table>
for demo check this code
I believe that the sample data supplied to the "rows" variable are missing.
So, I will make an imaginary assumption here that you have some web servers distributed in multiple locations and you want to manage access of users.
The following is my imaginary data for "rows" variable which is close enough to your data:
[
{
"serverID": 1,
"serverName": "My Backend API Server",
"locations": [
{
"id": 1,
"name": "SomePlace, CA",
"users": [
{ "id": 1, "name": "Person One" },
{ "id": 2, "name": "Person Two" }
]
},
{
"id": 2,
"name": "AnotherPlace, CA",
"users": [{ "id": 3, "name": "Person Three" }]
}
]
},
{
"serverID": 1,
"serverName": "My Frontend App Server",
"locations": [
{
"id": 1,
"name": "SomePlace, CA",
"users": [
{ "id": 1, "name": "Person One" },
{ "id": 2, "name": "Person Two" }
]
},
{
"id": 2,
"name": "AnotherPlace, CA",
"users": [{ "id": 3, "name": "Person Three" }]
}
]
}
]
Now, we have to loop over the servers array first, then loop over the locations array to get some thing close to your mock as follows:
Check this pen for the implementation.
JS Code:
Vue.component('v-select', VueSelect.VueSelect)
let servers = [
{
"serverID": 1,
"serverName": "My Backend API Server",
"locations": [
{
"id": 1,
"name": "SomePlace, CA",
"users": [
{ "id": 1, "name": "Person One" },
{ "id": 2, "name": "Person Two" }
]
},
{
"id": 2,
"name": "AnotherPlace, CA",
"users": [{ "id": 3, "name": "Person Three" }]
}
]
},
{
"serverID": 1,
"serverName": "My Frontend App Server",
"locations": [
{
"id": 1,
"name": "SomePlace, CA",
"users": [
{ "id": 1, "name": "Person One" },
{ "id": 2, "name": "Person Two" }
]
},
{
"id": 2,
"name": "AnotherPlace, CA",
"users": [{ "id": 3, "name": "Person Three" }]
}
]
}
];
let users = [
{"id":1, "name":"Person One"},
{"id":2, "name":"Person Two"},
{"id":3,"name":"Person Three"},
];
var app = new Vue({
el: '#app',
data() {
return {
errors: [],
loading: false,
selectedLocation: {},
rows: servers,
allUsers: users
}
}
})
HTML Code:
<div id="app">
<table>
<thead>
<tr>
<th class="col-xs-2">Locations</th>
<th class="col-xs-8">Users</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows" v-bind:key="index">
<td colspan="2">
<b>{{ row.serverName }}</b>
<table>
<tr v-for="(location, l_index) in row.locations" v-bind:key="l_index">
<td class="col-xs-2">{{ location.name }}</td>
<td class="col-xs-8">
<v-select multiple v-model="location.users" label="name" :options="allUsers">
</v-select>
</td>
</tr>
</table>
</td>
<td class="lead-locations">
{{ row.locations.name }}
</td>
<td class="lead-users">
</td>
</tr>
</tbody>
</table>
</div>

Custom table layout ng-repeat

I'm trying to figure out how to dynamically show a table with a JSON element but in a format that is hard to do with tables. I'm using AngularJs 1.6.4 and Bootstrap but I'm kind of new at Angular. My JSON:-
"foo": {
"name": "foo",
"displayName": "FOO SHOW",
"environments": [
{
"id": "one",
"name": "PROD",
"url": "http://my-prod-url.com"
},
{
"id": "two",
"name": "QA",
"url": "http://my-qa-url.com"
},
{
"id": "three",
"name": "DEV",
"url": "http://my-dev-url.com"
}
]
},
"bar": {
"name": "bar",
"displayName": "BAR SHOW",
"environments": [
{
"id": "four",
"name": "PROD",
"url": "https://my-prod2-url.com"
},
{
"id": "five",
"name": "QA",
"url": "https://my-uat2-url.com"
},
{
"id": "six",
"name": "DEV",
"url": "https://my-dev2-url.com"
}
]
}
I want to display this in a way that dynamically shows everything but based on application on the top and environment on the left of a table. For example:-
ENV | FOO | BAR
PROD| fooProdUrl| barProdUrl
QA | fooQaUrl| barQaUrl
DEV | fooDevUrl| barDevUrl
I've tried this but it's not dynamic and it doesn't display in the order I want:-
<table class="table">
<thead>
<tr>
<th class="text-center">Environment</th>
<th class="text-center" data-ng-repeat="list in applications">{{list.displayName}}</th>
</tr>
</thead>
<tbody class="text-center">
<tr data-ng-repeat="list in applications">
<th class="text-center">
{{list.environments[0].name}} and {{list.name}}
</th>
<td>
{{list.environments[0].url}} and {{list.name}} and {{list.environments[0].name}}
</td>
<td>
{{list.environments[1].url}} and {{list.name}} and {{list.environments[1].name}}
</td>
</tr>
</tbody>
</table>
This displays it in rows but not in the column way I want it.
Should I reorganize my JSON object? Split it in a different way? I've been stuck for a little bit on this.
Thanks!

Angular JS: How to use ng-repeat for complex objet

I have a complex object coming from the web service as shown below, how to display PatientId and description, if anyone have any good idea, please help me thru it.:
$scope.myData = {"PatientSearchResponse": {
"pageNumber": 1,
"pageSize": 50,
"patDataBean": [
{
"gender": {
"code": "male",
"description": "Male",
"type": "gender"
},
"patDateofBirth": "1997/06/19",
"patFirstName": "aman",
"patLastName": "elvis",
"patSurvivalStat": {
"code": "A",
"description": "Alive",
"type": "patient_status"
},
"patientIdentifier": {
"OID": "589a9cf6-4513-49e1-bd5c-c7363849ed93",
"organizationId": {
"PK": 54,
"siteName": "CTRP"
},
"patientId": "1"
}
},
{
"gender": {
"code": "male",
"description": "Male",
"type": "gender"
},
"patDateofBirth": "2001/07/18",
"patFirstName": "Elvis",
"patLastName": "Harvey",
"patSurvivalStat": {
"code": "dead",
"description": "Dead",
"type": "patient_status"
},
"patientIdentifier": {
"OID": "151d0222-3726-40ee-8f69-0a6800727607",
"organizationId": {
"OID": "83d09227-9c65-4d7b-94da-baaf5c07b38a",
"siteName": "Texas"
},
"patientId": "100"
}
}]}}
In my HTML I am using ng-repeat as:
<td ng-repeat="(key, value) in grid.columns">
<div>
<p >{{row[key]}}</p>
</div>
</td>
my JS file as:
myDataContainer = $scope.myData.PatientSearchResponse.patDataBean;
$scope.grid.columns = {patientIdentifier: "Patient Id",patFirstName: "First Name",patLastName: "Last Name",patDateofBirth: "Date of Birth",patSurvivalStat: "Description"};
angular.forEach(myDataContainer, function (values, index) {
$scope.grid.rows.push(values);
});
Why you can just do this :
<td ng-repeat="pat in myData.PatientSearchResponse.patDataBean">
{{pat.patientIdentifier}} - {{pat.patSurvivalStat.description}}
</td>
Try this man:
angular.module('app', [])
.controller('Controller', ['$scope', function($scope) {
$scope.myData = {"PatientSearchResponse": {
"pageNumber": 1,
"pageSize": 50,
"patDataBean": [
{
"gender": {
"code": "male",
"description": "Male",
"type": "gender"
},
"patDateofBirth": "1997/06/19",
"patFirstName": "aman",
"patLastName": "elvis",
"patSurvivalStat": {
"code": "A",
"description": "Alive",
"type": "patient_status"
},
"patientIdentifier": {
"OID": "589a9cf6-4513-49e1-bd5c-c7363849ed93",
"organizationId": {
"PK": 54,
"siteName": "CTRP"
},
"patientId": "1"
}
},
{
"gender": {
"code": "male",
"description": "Male",
"type": "gender"
},
"patDateofBirth": "2001/07/18",
"patFirstName": "Elvis",
"patLastName": "Harvey",
"patSurvivalStat": {
"code": "dead",
"description": "Dead",
"type": "patient_status"
},
"patientIdentifier": {
"OID": "151d0222-3726-40ee-8f69-0a6800727607",
"organizationId": {
"OID": "83d09227-9c65-4d7b-94da-baaf5c07b38a",
"siteName": "Texas"
},
"patientId": "100"
}
}]}}
}]);
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-controller="Controller">
<table>
<tr>
<th>Id</th>
<th>Description</th>
</tr>
<tr ng-repeat="m in myData.PatientSearchResponse.patDataBean">
<td>{{m.patientIdentifier.patientId}}</td>
<td>{{m.patSurvivalStat.description}}</td>
</tr>
</table>
</body>
</html>
Would be more practical to make columns an array of objects with standardized keys
$scope.grid.columns = [
{property : 'patientIdentifier', heading : "Patient Id"},
{property : 'patFirstName', heading : "First Name"},
{property : 'patLastName', heading : "Last Name"},
{property : 'patDateofBirth', heading : "Date of Birth"},
{property : 'patSurvivalStat', heading : "Description"}
]
Then use those to set both headings and content
<table>
<thead>
<tr>
<th ng-repeat="col in grid.columns">{{::col.heading}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in myData.patDataBean">
<td ng-repeat="col in grid.columns">{{::item[col.property]}}</td>
</tr>
</tbody>
</table>

Categories

Resources