Nested ng-repeat in AngularJS - html

I have the following JSON:
{ "ListNhomThanhTra": [
{
"ListKeHoachThanhTra": [
{
"ListDoiTuong": [
{
"MA_DV_DUOC_TT": "DT01",
"TEN_DOITUONG": "Tỉnh Bắc Ninh",
"stt": 1.1
}
],
"MA_DM_KEHOACH_TT": "KH13072018003",
"TEN_KEHOACH": "Kế hoạch chính thức",
"stt": 1
}
],
"MA_NHOM_TT": "010",
"TEN_NHOM": "Thanh tra ngân sách địa phương",
"stt": "I"
},
{
"ListKeHoachThanhTra": [
{
"ListDoiTuong": [
{
"MA_DV_DUOC_TT": "DT01",
"TEN_DOITUONG": "Tỉnh Bắc Ninh",
"stt": 1.1
}
],
"MA_DM_KEHOACH_TT": "KH13072018003",
"TEN_KEHOACH": "Kế hoạch chính thức",
"stt": 1
}
],
"MA_NHOM_TT": "02",
"TEN_NHOM": "Thanh tra tài chính, bộ ngành",
"stt": "II"
}
],
"MA_LOAI_TT": "01",
"TEN_LOAI": "Kế hoạch thanh tra",
"stt": "A"
}
Currently, I have a JSON string like this, and I want to display it in a tabular form.
I tried to use the code below, but it just stops at the second ng-repeat, and it's not going any further, so I was wondering how do I do it right?
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<tbody ng-repeat="loaiThanhTra in dataTable">
<td>{{loaiThanhTra.stt}}</td>
<td>{{loaiThanhTra.TEN_LOAI}}</td>
<tr ng-repeat="nhomThanhTra in loaiThanhTra.ListNhomThanhTra">
<td>{{nhomThanhTra.stt}}</td>
<td>{{nhomThanhTra.TEN_NHOM}}</td>
<tr ng-repeat="keHoachThanhTra in nhomThanhTra.ListKeHoachThanhTra ">
<td>{{keHoachThanhTra.stt}}</td>
<td>{{keHoachThanhTra.TEN_KEHOACH}}</td>
<tr ng-repeat="doiTuongThanhTra in keHoachThanhTra.ListDoiTuong">
<td>{{doiTuongThanhTra.stt}}</td>
<td>{{doiTuongThanhTra.TEN_DOITUONG}}</td>
</tr>
</tr>
</tr>
</tbody>
Please help me!!!

You cannot have trs in tds. Use ng-container and row spans on your parent objects.
<tbody ng-repeat="loaiThanhTra in dataTable">
<td>{{loaiThanhTra.stt}}</td>
<td>{{loaiThanhTra.TEN_LOAI}}</td>
<tr ng-repeat="nhomThanhTra in loaiThanhTra.ListNhomThanhTra">
<td rowspan="nhomThanhTra.ListKeHoachThanhTra.length">{{nhomThanhTra.stt}}</td>
<td rowspan="nhomThanhTra.ListKeHoachThanhTra.length">{{nhomThanhTra.TEN_NHOM}}
</td>
<ng-container ng-repeat="keHoachThanhTra in nhomThanhTra.ListKeHoachThanhTra ">
<td rowspan="keHoachThanhTra.ListDoiTuong.length">{{keHoachThanhTra.stt}}</td>
<td rowspan="keHoachThanhTra.ListDoiTuong.length">{{keHoachThanhTra.TEN_KEHOACH}}</td>
<ng-container ng-repeat="doiTuongThanhTra in keHoachThanhTra.ListDoiTuong">
<td>{{doiTuongThanhTra.stt}}</td>
<td>{{doiTuongThanhTra.TEN_DOITUONG}}</td>
</ng-container>
</ng-container>
</tbody>

Related

Angular: Dynamically find headers when converting JSON array to HTML table

In Angular, I want to convert a JSON array to an HTML table.
I have seen an old answer for AngularJS:
<table>
<thead>
<tr>
<th ng-repeat="(key, value) in records[0]">{{key}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="(key, value) in records">
<td ng-repeat="(key, value) in value">
{{value}}
</td>
</tr>
</tbody>
</table>
JSON looks like this:
[{
"Name": "Alfreds Futterkiste",
"City": "Berlin",
"Country": "Germany"
}, {
"Name": "Berglunds snabbköp",
"City": "Luleå",
"Country": "Sweden"
}, {
"Name": "Centro comercial Moctezuma",
"City": "México D.F.",
"Country": "Mexico"
}]
I've tried to translate it to the Angular syntax. Here is what I got so far:
<table>
<thead>
<tr>
<th *ngFor="let item of records[0] | keyvalue">{{item.key}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of records">
<td *ngFor="let item1 of item | keyvalue">
{{item1.value}}
</td>
</tr>
</tbody>
</table>
Right now it's failing to compile because records[0] is undefined... how can I translate this expression to the newer syntax (or create something equivalent)?
UPDATE 1:
I have a partial solution. However with this partial solution the rendered table is not completely identical to the older AngularJS rendition (because it creates multiple unnecessary header rows, which only one of them is populated, as opposed to only one header row in the older rendition).
<table style="border-collapse: collapse;">
<thead *ngFor="let item of records; let last=last">
<tr *ngIf="last">
<th *ngFor="let item1 of item | keyvalue">
{{item1.key}}
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of records">
<td *ngFor="let item1 of item | keyvalue">
{{item1.value}}
</td>
</tr>
</tbody>
</table>
Do you have a better way to do it, possibly similar to the older AngularJS version?
UPDATE 2:
In Angular, I access the JSON data through a request from Angular that is then redirected to a back end service. That service may read a file, or get the data from a database. When the back end service has the data ready, it returns the data to the Angular request. The code on the Angular end looks like this:
HTML:
<div>
<h3>Test Post Request</h3>
<button (click)="postData()">Click Me</button>
<div>Response: {{records}}</div>
</div>
TypeScript:
private dataPostTestUrl = '/api/postTest';
records: string | undefined;
public postData(): void {
this.appService.sendData().subscribe((data: any) => {
this.records = data.content;
});
}
public sendData(): Observable<any> {
return this.http.post(this.dataPostTestUrl, {});
}
I think maybe you need to define records in the component.
records = [{
"Name": "Alfreds Futterkiste",
"City": "Berlin",
"Country": "Germany"
}, {
"Name": "Berglunds snabbköp",
"City": "Luleå",
"Country": "Sweden"
}, {
"Name": "Centro comercial Moctezuma",
"City": "México D.F.",
"Country": "Mexico"
}];
I am fairly certain this is what you need: https://stackblitz.com/edit/angular-ivy-n7irpw?file=src/app/hello.component.ts
Take a look at how I import the file in the app.component.ts
As well as how I loop in the HTML.
Let me know if it helps!

Cannot print a table with *ngFor from a json

This particular Json does not have the key of each element in the arrays, I managed to make this work with other Jsons which bring the element keys and use those keys in the {{data.key}} to print with *ngFor.
I tried more than 25 solutions in stackoverflow but it seems that my case is unique, no one else uses a JSON without keys/labels to create a table.
This is my Json:
{
"data": {
"spnf": {
"anios": [
"2018-Q4",
"2018-Q4"
],
"titulos": [
"Ingresos Totales",
"Balance Total"
],
"anioactual": [
3183,
-837
],
"anioanterior": [
3672,
1549
]
},
"gob_central": {
"anios": [
"2018-Q4",
"2018-Q4"
],
"titulos": [
"Ingresos Totales",
"Balance Total"
],
"anioactual": [
3183,
-837
],
"anioanterior": [
3672,
1549
]
}
}
}
This is my balances.ts:
{
this.loader.present().then(() => {
this.finanzaService.getBalances2()
.subscribe((result) => {
this.dataRequest = result;
this.setData();
// Disable Loader
this.loader.dismiss();
}, (err) => {
"error blah blah"
}
);
});
}
public setData(tipo: string = 'spnf') {
if (tipo == 'spnf') {
this.dataResumen = _.clone(this.dataRequest.spnf);
} else {
this.dataResumen = _.clone(this.dataRequest.gob_central);
}
}
This is my finanzapublica.service.ts:
public getBalances2(): Observable<any>{
const url = `${this.apiURL}/balances_fiscales/balances_datos2.json`;
return this.http.get(url, this.options)
.map((res: Response) => res.json().data);
}
This is my balances.html, as soon as I try this.dataResumen, the app breaks and throws error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.
<table *ngIf="dataRequest">
<thead>
<tr>
<td class="border" text-center>Rubro</td>
<td class="border" *ngFor="let data of this.dataResumen.anios" text-center>{{ data }}</td> <!--this works-->
</tr>
</thead>
<tbody>
<tr *ngFor="let data of this.dataResumen"><!--this doesn't work-->
<td class="border" text-center>{{data.titulos}}</td>
<td class="border" text-center>{{data.anioactual}}</td>
<td class="border" text-center>{{data.anioanterior}}</td>
</tr>
</tbody>
</table>
This is the error:
Cannot find a differ supporting object '{
"anios": [
"2018-Q4",
"2018-Q4"
],
"titulos": [
"Ingresos Totales",
"Balance Total"
],
"anioactual": [
3183,
-837
],
"anioanterior": [
3672,
1549
]
}' of type 'string'. NgFor only supports binding to Iterables such as Arrays.
The result I wanted is like:
Rubros 2018-Q4 2018-Q4
Ingresos Totales 3183 3672
Balance Total -837 1549
<table>
<thead>
<tr>
<td class="border" text-center>Rubro</td>
<td class="border" *ngFor="let data of dataResumen?.anios" text-center>{{ data }}</td> <!--this works-->
</tr>
</thead>
<tbody>
<tr *ngFor="let data of dataResumen.titulos; let i=index;">
{{data}}
<td>{{dataResumen.anioactual[i]}}</td>
<td>{{dataResumen.anioanterior[i]}}</td>
</tr>
</tbody>
</table>

Unable to iterate *ngFor loop with array of column in table

I have table like this which i want to iterate
Heating Diesel Benzin Fracht
43.10 87.15 108.00
43.35 87.40 108.25
43.80 87.80 108.60 2394.00
----- ----- ----- ----
Here is json data which i want to insert into my table and here first object is one column, second object is second column like that i need to insert 4 columns. i don't understand how to iterate this with *ngFor loop. please excuse if any thing wrong in my question.
[
{
"s4": "43,10",
"s5": "43,35",
"s6": "43,80",
"s7": "43,90",
"s8": "44,10",
"s15": "64,25",
"s9": "44,55",
"s10": "43,20",
"s11": "43,90",
"s16": "54,00"
},
{
"s4": "87,15",
"s5": "87,40",
"s6": "87,80",
"s7": "87,90",
"s8": "88,05",
"s15": "121,05",
"s9": "88,60",
"s10": "87,30",
"s11": "88,00",
"s16": "80,90"
},
{
"s4": "108,00",
"s5": "108,25",
"s6": "108,60",
"s7": "108,70",
"s8": "108,85",
"s15": "119,65",
"s9": "109,30",
"s10": "108,50",
"s11": "109,00",
"s16": "92,25"
},
{
"s4": "",
"s5": "",
"s6": "2394,02",
"s7": "12,29",
"s8": "2395,46",
"s15": "",
"s9": "2386,92",
"s10": "22:05",
"s11": "",
"s16": ""
}
]
<table *ngIf="indexdata">
<tr style="color: #3B6593" >
<th><strong>Heizöl</strong></th>
<th><strong>Diesel</strong></th>
<th><strong>Benzin</strong></th>
<th><strong>Facht</strong></th>
</tr>
<tr *ngFor="let index of indexdata;let i=index">
<td>{{index.s4}}</td>
<td>{{index.s5}}</td>
<td>{{index.s6}}</td>
-----
-----
</tr>
</table>
Your code should be
<table *ngIf="indexdata">
<tr style="color: #3B6593">
<th><strong>Heizöl</strong></th>
<th><strong>Diesel</strong></th>
<th><strong>Benzin</strong></th>
<th><strong>Facht</strong></th>
</tr>
<tr>
<td *ngFor="let indexvalue of indexdata">
<table>
<tr *ngFor="let indexobj of indexvalue | keys">
<td>
{{indexobj.value}}
</td>
</tr>
</table>
</td>
</tr>
</table>
You could create a custom pipe to return the list of key for each element. Something like this:
#Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
transform(value, args:string[]) : any {
let keys = [];
for (let key in value) {
keys.push({key: key, value: value[key]});
}
return keys;
}
}

present nested data in table using angularjs

Using angular 1.4.7
I have the following javascript object
var parents = [
{"name" : "John",
childs:[{ "name" : "Child1", age:"4"},{"name" : "Child2",age:"8"}]},
{"name" : "Peter",
childs:[{ "name" : "Child3", age:"5"},{"name" : "Child", age:"12"}]}
]
and I want to have the following output as html table
<table>
<tr>
<td>John</td>
<td>Child1</td>
<td>4</td>
</tr>
<tr>
<td></td>
<td>Child2</td>
<td>8</td>
</tr>
<tr>
<td>Peter</td>
<td>Child3</td>
<td>5</td>
</tr>
<tr>
<td></td>
<td>Child3</td>
<td>12</td>
</tr>
</table>
I have been trying using ng-repet-start to insert a new row (</tr><tr>) within the child loop.
<tr ng-repeat-start="parent in parents">
<td >
{{parent.name}}
</td>
<td ng-repeat-start="child in parent.childs">
{{child.name}}
</td>
<td>
{{child.age}}
<!--new row within child loop and -->
</tr>
<tr>
<td></td>
<td ng-repeat-end></td>
<tr ng-repeat-end></tr>
The error I get in the console is:
Unterminated attribute, found 'ng-repeat-start' but no matching 'ng-repeat-end' found.
If I take out the </tr><tr> I get no error, but also not the desired output.
This code will give you almost the output you've asked (some extra <tbody> tags there, but it's still valid html):
<table>
<tbody ng-repeat="parent in parents">
<tr ng-repeat="child in parent.childs">
<td ng-if = "$index == 0">{{parent.name}}</td>
<td ng-if = "$index != 0"></td>
<td >
{{child.name}}
</td>
<td>{{child.age}}</td>
</tr>
</tbody>
</table>
Here is working Plunk
I should say, that implementation is quite ugly. I would used <td rowspan="number_of children"> to achieve similar visual result. See implementation here, with rowspan and a little bit of bootstrap.
<table class = "table table-condensed">
<tbody ng-repeat="parent in parents">
<tr ng-repeat="child in parent.childs">
<td ng-if="$index == 0" rowspan="{{parent.childs.length}}">{{parent.name}}</td>
<td>
{{child.name}}
</td>
<td>{{child.age}}</td>
</tr>
</tbody>
</table>
EDIT: updated Plunker
Dirty implementation for data with 'grandchildren'
$scope.withGrandChildren = [
{"name" : "John",
childs:[
{ "name" : "Child1",
age:"42",
grandchildrens:[
{"name":"Graddchildren1_1", age:"12"} ,
{"name":"Grandchildren1_2", age:"17"}
]
},
{ "name" : "Child2",
age:"38",
grandchildrens:[
{"name":"Graddchildren2_1", age:"19"}
]
}
]
},
{"name" : "Peter",
childs:[
{ "name" : "Child3", age:"45"},
{"name" : "Child", age:"12"}
]
}
]
Try this instead:
<tbody ng-repeat="parent in parents">
<tr ng-repeat="child in parent.childs">
<td>{{parent.name}}</td>
<td >
{{child.name}}
</td>
<td>{{child.age}}</td>
</tr>
</tbody>
The only difference of this with your desired output is that the parent name is displayed for all child rows

Dynamic table in knockoutjs

I am using ajax call to get data its return data in json format like below:
{ "d" : [
{ "Goal" : "Some one client",
"GoalID" : 1,
"IsPublic" : true,
"MemberName" : "user1"
},
{ "Goal" : " this is goal",
"GoalID" : 1,
"IsPublic" : false,
"MemberName" : "user2"
},
{ "Goal" : "Get call",
"GoalID" : 4,
"IsPublic" : true,
"MemberName" : "user2"
}
] }
Now in my html page i want to create separate table for each user. Here is my table
//want username here then table for goal of that user
<table>
<thead>
<tr class="headerRow">
<th>member</th>
<th>
Goals
</th>
</tr>
</thead>
<tbody data-bind="foreach: tasks">
<tr">
<td>
<span data-bind="text: members" />
</td>
<td>
<span data-bind="text: goal" />
</td>
</tr>
</tbody>
</table>
And "task" is the observable array containing all goal details.
This is my goal constuctor in javascript
function Goal(data) {
var self = this;
self.goalID = data.GoalID;
self.goal = data.Goal;
self.isPublic = ko.observable(data.IsPublic);
self.members = ko.observable(data.MemberName);
}
Maybe it is not the best solution but it will work. Add computed that returns all users:
self.users = ko.computed(function(){
var list = ko.utils.arrayMap(self.tasks(), function(item){
return item.members
});
return ko.utils.arrayGetDistinctValues(list);
});
And using foreach binding create a table for each user:
<div data-bind="foreach: users">
<table>
<thead>
<tr>
<th>membername</th>
<th>
Goals
</th>
</tr>
</thead>
<tbody data-bind="foreach: $parent.tasks">
<tr data-bind="if: members == $parent">
<td>
<span data-bind="text: members" />
</td>
<td>
<span data-bind="text: goals" />
</td>
</tr>
</tbody>
</table>
</div>
Here is working fiddle: http://jsfiddle.net/TQXja/4/