Create a dynamic Table out of JSON Object using Angular 5 directives - javascript

Is it possible to create a dynamic Table(dynamic Columns) based on a JSON object with Angular 5 directives/ Or with the help of jQuery? If so how?
Lets say i get this JSON response from a REST API:
{
name: "Ferrari"
country: "Italy",
creater: "Enzo Ferrari"
cars: [
{
modell: "Ferrari 488",
price: "215.683€"
},
{
modell: "Ferrari Portofino",
price: "189.704€"
}
]
}
Now i want to create a Table out of this Data that should look like this:
+-----------+----------+--------------+--------------+------------+--------------------+-------------+
| Name | Country | Creater | Modell | Price | Modell | Price |
+-----------+----------+--------------+--------------+------------+----------------------------------+
| Ferrari | Italy | Enzo Ferrarie| Ferrarie 488 | 189.704€ | Ferrarie Portofino | 189.704€ |
+-----------+----------+--------------+--------------+------------+--------------------+-------------+
What would the best approach be to solve this? I just cant figure it out how to solve this problem? Any help is really appreciated.

If you only have one object in the table (meaning one header row and one data row) you could do something like this:
<table>
<thead>
<tr>
<th>Name</th>
<th>Country</th>
<th>Creater</th>
<ng-template ngFor [ngForOf]="data.cars">
<th>Modell</th>
<th>Price</th>
</ng-template>
</tr>
</thead>
<tbody>
<tr>
<td>{{ data.name }}</td>
<td>{{ data.country }}</td>
<td>{{ data.creater }}</td>
<ng-template ngFor let-car [ngForOf]="data.cars">
<th>{{ car.modell }}</th>
<th>{{ car.price }}</th>
</ng-template>
</tr>
</tbody>
</table>
You can us ng-template and the ngFor directive to loop over the cars.
Important: If you have multiple data rows you need to make sure to transform your data so you always display the same amount of columns, otherwise the html will be broken.

Related

PrimeNG Table body doesn't display data

I am having trouble trying to fetch data to my overlay.Where did I go wrong ?
My goal is to fetch from an user array ,data (like cId,logged hours) and display it inside my table.
<button
[disabled]="false"
(click)="op1.show($event)"
pButton
type="button"
class="ui-button-primary"
label="View"
></button>
<p-overlayPanel #op1 [showCloseIcon]="true" [dismissable]="false">
<p-table [value]="day" [style]="{ width: '400px' }" [rows]="5">
<ng-template pTemplate="header">
<tr>
<td class="headerItem">{{ wbsElement }}</td>
<td class="headerItem">{{ saturday }}</td>
<td class="headerItem">{{ sunday }}</td>
...
<td class="headerItem">{{ total }}</td>
</tr>
</ng-template>
<ng-template pTemplate="body" let-day>
<tr>
<td>{{ user.cId }}</td>
<td *ngFor="let day of user.days">{{ day.hoursLogged }}</td>
<td>{{ totalHours }}</td>
</tr>
</ng-template>
</p-table>
</p-overlayPanel>
Above I've got my template that is supposed to create my overlay as shown here
wbsElement = Constants.WBS_ELEMENT;
saturday = Day.SATURDAY;
sunday = Day.SUNDAY;
monday = Day.MONDAY;
...
total = Constants.TOTAL;
user: User;
totalHours: number = 0;
constructor(private configService: ConfigService) {}
ngOnInit() {
this.configService.getUsers().subscribe(users => {
this.user = users[0];
});
this.getTotalHours();
}
private getTotalHours() {
this.user.days.forEach(day => (this.totalHours += day.hoursLogged));
}
This is what I have inside my ts file. user accepts dummy data from the first element of users array. Here's a preview of what it receives:
{
name: "test",
cId: "akaskdasda",
email: "test#test.com",
platformUser: "akakaksda",
days: [
{ weekday: Day.MONDAY, hoursLogged: 5 },
{ weekday: Day.TUESDAY, hoursLogged: 8 },
...
{ weekday: Day.SUNDAY, hoursLogged: 5 }
]
},
Currently ,there's no error is displayed inside console that would help me find out the reason of this behaviour and the output is not desired .
As you can see inside the imgur link , the elements go outside of the table (I dont understand this behaviour). I tried removing my double bindings from table body and printing out random data to see if that would work(I've also removed [value]="day" and let-day when I did this) ,however nothing was shown.Last thing I've tried was replacing ng-template with divs (because essentially that's what they are -as far as my understanding goes) but that didnt go well either.
It is correct that PrimeNg requires a [value] in order to create a static table.
However, you can pass in an empty array and do everything else statically as desired. So if inside the <p-table... you add [value]="[[]]", you can set up everything else statically inside the <tr>s and <td>s.
Figure I would post this here in case anyone else has a similar issue before giving up on the p-table altogether.
Managed to solve this by ditching p-table and using a normal table instead. Apparently , as I found here ,p-table expects an array because of [value]="user".
You have to assign user variable to the table value.
Try this.
<p-overlayPanel #op1 [showCloseIcon]="true" [dismissable]="false">
<p-table [value]="user" [style]="{ width: '400px' }" [rows]="5">
<ng-template pTemplate="header">
<tr>
<td class="headerItem">{{ wbsElement }}</td>
<td class="headerItem">{{ saturday }}</td>
<td class="headerItem">{{ sunday }}</td>
...
<td class="headerItem">{{ total }}</td>
</tr>
</ng-template>
<ng-template pTemplate="body" let-user>
<tr>
<td>{{ user.cId }}</td>
<td *ngFor="let day of user.days">{{ day.hoursLogged }}</td>
<td>{{ totalHours }}</td>
</tr>
</ng-template>
</p-table>
</p-overlayPanel>

Get place in table of icon using ng-click

I have a table that's generated using ng-repeat with some columns headers. On the column headers, I want to columns to be sorted. I included a font-awesome sort icon next to their text and when it's clicked, I call an ng-click with ng-click="sortColumn()". I'm calling this for every column header.
How do I know which column header got clicked on? Right now it fires for each header column.
How do pass the current column in using ng-click?
I tried to pass in this, but it returns the $scope object.
Here's what I have so far:
HTML:
<table class="table">
<thead>
<tr>
<td><b>Product </b><i class="fa fa-sort" ng-click="vm.sortColumn(this)"></i></td>
<td><b>Code </b><i class="fa fa-sort" ng-click="vm.sortColumn(this)"></i></td>
<td><B>Available </b><i class="fa fa-sort" ng-click="vm.sortColumn(this)"></i></td>
<td><B>Price </b><i class="fa fa-sort" ng-click="vm.sortColumn(this)"></i></td>
</tr>
</thead>
<tbody>
{{ vm.noProducts }}
<tr ng-repeat="product in vm.products">
<td>{{ product.productName}}</td>
<td>{{ product.productCode }}</td>
<td>{{ product.releaseDate | date }}</td>
<td>{{ product.price | currency }}</td>
</tr>
</tbody>
</table>
Javascript:
vm.sortColumn = function (obj) {
console.log(obj);
}
I don't think that you can just pass column as object to sortColumn(). If you need column in that function you can loop trough products and make a new array that will be populated for example with all product names.
Maybe that function could look like this:
function getColumn(val){ // 'productName'
var column = [];
for(var i in $scope.products){
column.push(column.push($scope.products[i][val]))
}
return column
}
If you want to sort table you can use orderBy filter.
To your sortColumn() function pass parameter name and in your ng-repeat use orderBy to order column ascending/descending by one of the parameters.
<table class="table">
<thead>
<tr>
<td ng-click="sortColumn('productName')"><b>Product </b></td>
<td ng-click="sortColumn('productCode')"><b >Code </b></td>
<td ng-click="sortColumn('releaseDate')"><b >Available </b></td>
<td ng-click="sortColumn('price')"><b >Price </b></td>
</tr>
</thead>
<tbody>
<tr ng-repeat="product in products | orderBy: criteria">
<td>{{ product.productName}}</td>
<td>{{ product.productCode }}</td>
<td>{{ product.releaseDate | date }}</td>
<td>{{ product.price | currency }}</td>
</tr>
</tbody>
</table>
In your controller set default value for orderBy criteria. (I used here productName)
$scope.criteria = 'productName';
$scope.sortColumn = function(val){
console.log(val)
if($scope.criteria == val){
$scope.criteria = "-"+val;
} else {
$scope.criteria = val;
}
If user clicks on productCode for example, all rows will be sorted by that criteria ascending ('productCode'), if user click on productCode once again all will be sorted descending by criteria '-productCode'
I created small plunker to demonstrate that. Hope it helps.

Displaying navigation properties in ngx-datatable

I have a table like this that displays data including several navigation properties :
<table class="table afcstandings">
<thead>
<tr>
<th>team</th>
<th>coach</th>
<th>w</th>
<th>l</th>
<th>t</th>
<th>fa</th>
<th>agst</th>
<th>diff</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let standing of standingsAFCEast">
<!-- property binding rather than interpolation-->
<td>{{ standing.team.teamName }}</td>
<td>{{ standing.team.coach.coachName }}</td>
<td>{{ standing.won }}</td>
<td>{{ standing.lost }}</td>
<td>{{ standing.tied }}</td>
<td>{{ standing.pointsFor }}</td>
<td>{{ standing.pointsAgainst }}</td>
<td>{{ standing.pointsDifference }}</td>
</tr>
</tbody>
</table>
Here is the data structure that is being read :
[{"team":{"teamId":22,"teamName":"Carolina Panthers","coach":{"coachId":61,"coachName":"J Smith"},"division":{"divisionId":2,"divisionName":"NFC West"},"headerImage":"","logoImage":"","hex":"","r":null,"g":null,"b":null},"won":2,"lost":1,"tied":0,"pointsFor":82,"pointsAgainst":62,"pointsDifference":20}]
My question is, how do I display this data using ngx-datatable? I have tested with 3 fields, teamName, coachName and won, and am able to display the won field, but not the others, as I am not sure how to drill down into the team object or the coach object.
<ngx-datatable class="ngx-datatable" [rows]="standingsAFCEast">
<ngx-datatable-column name="team.teamName" [width]="300"></ngx-datatable-column>
<ngx-datatable-column name="team.coach.coachName"></ngx-datatable-column>
<ngx-datatable-column name="won"></ngx-datatable-column>
</ngx-datatable>
Any advice would be really appreciated!
After looking at the basic examples, I made this work (Plunker here):
#Component({
selector: 'my-app',
template: `
<div>
<ngx-datatable
[rows]="rows"
[columns]="columns"
[columnMode]="'force'"
[headerHeight]="50"
[footerHeight]="50"
[rowHeight]="'auto'"
[reorderable]="reorderable">
</ngx-datatable>
</div>
`
})
export class AppComponent {
standingsAFCEast = [{
"team":{
"teamId":22,
"teamName":"Carolina Panthers",
"coach":{
"coachId":61,
"coachName":"J Smith"
},
"division":{
"divisionId":2,
"divisionName":"NFC West"
},
"headerImage":"",
"logoImage":"",
"hex":"",
"r":null,
"g":null,
"b":null
},
"won":2,
"lost":1,
"tied":0,
"pointsFor":82,
"pointsAgainst":62,
"pointsDifference":20
}]
get rows () {
return this.standingsAFCEast.map(standing => ({
team: standing.team.teamName,
coach: standing.team.coach.coachName,
w: standing.won,
l: standing.lost,
t: standing.tied,
fa: standing.pointsFor,
agst: standing.pointsAgainst,
diff: standing.pointsDifference
}))
}
// columns = [{name:'team'}, {name:'coach'}, {name:'w'}, {name:'l'}, {name:'t'}, {name:'fa'}, {name:'agst'}, {name:'diff'}]
columns = Object.keys(this.rows[0]).map(val => ({name: val}))
}
Let me know if this helps!

Searching through JSON Object with ng-class (AngularJS)

I have two JSON objects defined in a controller (NotificationsController). One with all the notifications and another one with only the ID of the newest notifications (last 3 days).
Format of object "notifications": (t_notifications)
[{"0":"1","1":"4","2":"14-APR-16","3":"ALERT 1","ID":"1","ID_USER":"4","DATE":"14-APR-16","NOTIFICATION":"ALERT 1!"},{"0":"2","1":"1","2":"07-APR-16","3":"ALERT 2!","ID":"2","ID_USER":"1","DATE":"07-APR-16","NOTIFICATION":"ALERT 2!"},{"0":"3","1":"1","2":"13-APR-16","3":"ALERT 3!","ID":"3","ID_USER":"1","DATE":"13-APR-16","NOTIFICATION":"ALERT 3!"}]
Format of object "newest notifications": (newest_notifications)
[{"0":"1","ID_NEWNOTIF":"1"},{"0":"3","ID_NEWNOTIF":"3"}]
I'm displaying all the notifications in a view like this:
<div class="panel-body" ng-controller="NotificationsCtrl">
<table datatable="ng" class="row-border hover">
<thead>
<tr>
<th><b>ID</b> </th>
<th><b>ID_USER</b> </th>
<th><b>DATE</b> </th>
<th><b>NOTIFICATION</b> </th>
</tr>
</thead>
<tbody>
<tr ng-repeat="data in t_notifications" ng-class="{selected: data.ID == **TO COMPLETE**>
<td>{{ data.ID }}</td>
<td>{{ data.ID_USER }}</td>
<td>{{ data.DATE }}</td>
<td>{{ data.NOTIFICATION }}</td>
</tr>
</tbody>
</table>
</div>
I would like to know how it is possible to select in my table only the newest notifications - searching through the JSON object newest_notifications - with ng-class?
PS: "selected" is already defined with a blue background color.
Just use a strict filter
... ng-class="{'selected': (newest_notifications | filter : { ID_NEWNOTIF: data.ID } : true).length !== 0 }"
Fiddle - https://jsfiddle.net/dyxf5xqj/
You can use a expression against data.DATE. For example
<tr ng-repeat="data in t_notifications" ng-class="{selected: data.DATE > someDateInThePast}">

Angular Dir-Pagination and calling controller function

I have an angular html snippet below of a table I am trying to wire up using Dir-Paginate and also have the table row change class based on a function on the controller.js
<tr dir-pagination="response in vm.responses | toArray | orderBy:'-completionTime' | itemsPerPage: $root.pagination.itemsPerPage" class="{{ vm.getRowClass($index) }}">
<td> {{ $index + 1 }}</td>
<td><a ui-sref="response({uri:response.uri})">{{ response.name }}</a></td>
<td>{{ response.email }}</td>
<tr>
<dir-pagination-controls ng-show="appvm.showDataTable()" boundary-links="true" on-page-change="pageChangeHandler(newPageNumber)" template-url="ng/pagination"></dir-pagination-controls>
If i remove all of the Dir-Pagination syntax and make it just a normal ng-repeat the vm.getRowClass($index) works as it should, changing the class based on the function's logic. However, when I try to add the dir-pagination syntax in, i am given a $Index is undefined error.
Problem seems to be that Dir-Paginate does not allow me to pass in an $index or any item on the same line as the <tr>
After the comment and some playing around i found the solution.
I needed to add track by $index to the end of the itemsPerPage: $root.pagination.itemsPerPage area.
Shown correctly below.
<tr dir-pagination="response in vm.responses | toArray | orderBy:'-completionTime' | itemsPerPage: $root.pagination.itemsPerPage track by $index" class="{{ vm.getRowClass($index) }}">
<td> {{ $index + 1 }}</td>
<td><a ui-sref="response({uri:response.uri})">{{ response.name }}</a></td>
<td>{{ response.email }}</td>
<tr>

Categories