v-for seems to change the array order - javascript

Data Object:
{
"headers": {
"location": "Location",
"postcode": "Postcode",
"contributors": "Contributors",
"contributions": "Contributions",
"percentage": "Percentage"
},
"rows": [
{
"postcode": "3018",
"contributors": 2,
"contributions": 2,
"location": "Seaholme",
"percentage": 67
},
{
"postcode": "3013",
"contributors": 1,
"contributions": 1,
"location": "Yarraville West",
"percentage": 33
}
]
}
Template:
<thead>
<tr>
<th v-for="(v, k) in data.result.headers" :key="k">
{{ v }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in data.result.rows" :key="i">
<td :key="j" v-for="(col, j) in row">
{{ col }}
</td>
</tr>
</tbody>
Output:
So the table header and body are two separate objects. While the header seems to follow the order but the row objects don't. How can I make sure they always align correctly?

You can create a computed property of the rows. This would be the same list but with the keys ordered in the order of the header keys. Here is a possible solution:
new Vue({
el: "#app",
data: () => ({
"headers": { "location": "Location", "postcode": "Postcode", "contributors": "Contributors", "contributions": "Contributions", "percentage": "Percentage" },
"rows": [
{ "postcode": "3018", "contributors": 2, "contributions": 2, "location": "Seaholme", "percentage": 67 },
{ "postcode": "3013", "contributors": 1, "contributions": 1, "location": "Yarraville West", "percentage": 33 }
]
}),
computed: {
orderedRows() {
const headers = Object.keys(this.headers);
return this.rows.map(row =>
headers.reduce((orderedRow, key) =>
({ ...orderedRow, [key]: row[key] })
, {})
);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<thead>
<tr>
<th v-for="(v, k) in headers" :key="k">{{ v }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in orderedRows" :key="i">
<td v-for="(col, j) in row" :key="j">{{ col }}</td>
</tr>
</tbody>
</table>
</div>
Another possible way inspired from #CertainPerformance comment:
new Vue({
el: "#app",
data: () => ({
"headers": { "location": "Location", "postcode": "Postcode", "contributors": "Contributors", "contributions": "Contributions", "percentage": "Percentage" },
"rows": [
{ "postcode": "3018", "contributors": 2, "contributions": 2, "location": "Seaholme", "percentage": 67 },
{ "postcode": "3013", "contributors": 1, "contributions": 1, "location": "Yarraville West", "percentage": 33 }
]
}),
computed: {
headerKeys() {
return Object.keys(this.headers);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<thead>
<tr>
<th v-for="(v, k) in headers" :key="k">{{ v }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in rows" :key="i">
<td v-for="(header, j) in headerKeys" :key="j">{{ row[header] }}</td>
</tr>
</tbody>
</table>
</div>

Related

Parsing JSON with data with varing fields

I am trying to polulate a html table using the JSON reponse. I have parsed and use angluar ng-repeat to fill the table. But now I noticed that the JSON reponse type may vary based on the type of user.
JSON response:
[{
"id": 133,
"account": {
"accountNumber": "6335050000000111",
"accountType": "TSys",
"address": {
"companyName": "",
"street1": "",
"street2": "",
"street3": "",
"street4": "",
"country": "GB",
"prepayBalance": 110
}
},
"lastModifiedDate": "Apr 09, 2020",
"lastModifiedBy": "uk#gmail.com"
}, {
"id": 132,
"account": {
"accountNumber": "6335052400000034",
"accountType": "PREPAY",
"address": {
"companyName": "",
"street1": "",
"street2": "",
"street3": "",
"street4": "",
"country": "GB",
"prepayBalance": 130
},
"bankAccount": {
"id": 54390,
"bankAccountName": "ABC Coompany",
"bankAccountNumber": "3245",
"sortCode": "12-34-56",
"address": {
"street1": "Fidelity Bank",
"street2": "10 BANK STREET",
"street3": "LONDON",
"street4": "SW1A 2AA",
"country": "GB"
}
}
},
"lastModifiedDate": "Apr 09, 2020",
"lastModifiedBy": "ukcase#mailinator.com"
}]
As you can see in the response some may or may not receive "bankAccount" as response. I only need to select those which has bank account but at the same time count all the records.
Example:
count(data.account.accountType) //somehow implement the count logic
count(data.account.bankAccount)
// Main logic
$scope.getAllDd = data.account.bankAccount; // get only those records with bankaccount in response
Which I can use for:
<tr ng-repeat="ddAccounts in getAllDd">
<td>{{ddAccounts.account.accountType}}</td>
Declare the unfiltered accounts separate from the filtered ones.
const app = angular.module('AccountApp', []);
const hasBankAccount = (account) => account.account.bankAccount != null;
app.filter('objectValues', () => {
return (obj) => Object.values(obj).join(', ');
});
app.controller('AccountController', function($scope, $http) {
$scope.filters = {
search: ''
};
// Filter here initially...
$scope.accounts = getJson();
$scope.filteredData = $scope.accounts.filter(hasBankAccount);
// Filter even more on search...
$scope.doSearch = function() {
let filterField = document.querySelector('#txt-filter');
let filterText = filterField.value.trim().toLowerCase();
$scope.filteredData = filterText.length === 0
? $scope.accounts.filter(hasBankAccount)
: $scope.filteredData = $scope.accounts
.filter(hasBankAccount)
.filter(account => {
return account.account.accountNumber.toLowerCase().includes(filterText);
});
};
});
function getJson() {
return [{
"id": 133,
"account": {
"accountNumber": "6335050000000111",
"accountType": "TSys",
"address": {
"companyName": "",
"street1": "",
"street2": "",
"street3": "",
"street4": "",
"country": "GB",
"prepayBalance": 110
}
},
"lastModifiedDate": "Apr 09, 2020",
"lastModifiedBy": "uk#gmail.com"
}, {
"id": 132,
"account": {
"accountNumber": "6335052400000034",
"accountType": "PREPAY",
"address": {
"companyName": "",
"street1": "",
"street2": "",
"street3": "",
"street4": "",
"country": "GB",
"prepayBalance": 130
},
"bankAccount": {
"id": 54390,
"bankAccountName": "ABC Company",
"bankAccountNumber": "3245",
"sortCode": "12-34-56",
"address": {
"street1": "Fidelity Bank",
"street2": "10 BANK STREET",
"street3": "LONDON",
"street4": "SW1A 2AA",
"country": "GB"
}
}
},
"lastModifiedDate": "Apr 09, 2020",
"lastModifiedBy": "ukcase#mailinator.com"
}];
}
table, th, td {
border: 1px solid grey;
border-collapse: collapse;
padding: 0.25em;
font-size: 0.9em;
}
table {
width: 100%;
}
.toolbar {
margin-bottom: 1em;
}
.details {
margin-top: 1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="AccountApp">
<div ng-controller="AccountController">
<div class="toolbar">
<input type="button" value="Filter" ng-model="query" ng-model-options="{ debounce: 200 }" ng-click="doSearch()">
<input type="text" id="txt-filter">
</div>
<table>
<thead>
<tr>
<th>#</th>
<th>Acct. No.</th>
<th>Acct. Type</th>
<th>Address</th>
<th>Bank Name</th>
<th>Bank Acct. No.</th>
<th>Bank Address</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="account in filteredData | orderBy : 'account.accountNumber' | filter : filters.search">
<td>{{ $index + 1 }}</td>
<td>{{ account.account.accountNumber }}</td>
<td>{{ account.account.accountType }}</td>
<td>{{ account.account.address.country }}</td>
<td>{{ account.account.bankAccount.bankAccountName }}</td>
<td>{{ account.account.bankAccount.bankAccountNumber }}</td>
<td>{{ account.account.bankAccount.address | objectValues }}</td>
</tr>
</tbody>
</table>
<div class="details">
<strong>No. of Accounts: </strong>
<span ng-bind="filteredData.length"></span> /
<span ng-bind="accounts.length"></span>
</div>
</div>
</div>
have you tried with .filter() methods ? ... something like:
$scope.getAllDd = data.account.filter(xx=> xx. bankAccount)
this.totalRecords = data.account.length;
and then
<tr ng-repeat="ddAccounts in getAllDd">
<td>{{ddAccounts.account.accountType}}</td>
Hope it helps you!!

Populating table based on dynamic created headers AngularJs

First to introduce to my table structure:
<div class="col-md-9">
<table class="table table-bordered">
<thead>
<tr>
<th>Date</th>
<th>Pair</th>
<th id="{{odds.id}}" ng-repeat="odds in list">{{odds.odd.name}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="match in matches ">
<td>{{match.sd | parseMomentDate}}</td>
<td>{{match.h}}-{{match.a}}</td>
<td>{{match.odds.drawNoBet.value}}<span ng-if="!match.drawNoBet.value">--</span></td>
</tr>
</tbody>
</table>
</div>
I am listing odd names, and therefore setting up id attr for the element.
So my problem is how to populate the rest of the table, the table data based on the Id from the table head, every table head to have rows and columns populated based on that id?
I have the next structure of receiving the data:
"id": 4413011,
"sd": "2017-04-27T23:30:00.000+0400",
"h": "Athletic Bilbao",
"a": "Betis Sevilla",
"odds": {
"bothToScore": [
{
"name": "no",
"value": 1.85,
"id": 552240303
},
{
"name": "yes",
"value": 1.95,
"id": 552240338
}
],
"doubleChance": [
{
"name": "12",
"value": 1.22,
"id": 552240012
},
{
"name": "x2",
"value": 2.98,
"id": 552240003
},
{
"name": "1x",
"value": 1.11,
"id": 552240079
}
],
"drawNoBet": [
{
"name": "1",
"value": 1.15,
"id": 552240007
},
{
"name": "2",
"value": 6.15,
"id": 552240267
}
],
"totalGoals": [
{
"name": "under",
"value": 2.15,
"id": 552235662
},
{
"name": "over",
"value": 1.7,
"id": 552235663
}
]
}
},
So for example in the odds object there is a list "bothToScore", therefore "bothToScore" is the id for the table head, and based on that id need to populate the rest of the columns, meaning ;Yes' or 'No'.
Any suggestions?
At this case id mapping is not needed - just ensure same order of <th>s and <tr>s:
angular.module('app', []).controller('ctrl', ['$scope', function($scope) {
$scope.matches = [{
"id": 4413011,
"sd": "2017-04-27T23:30:00.000+0400",
"h": "Athletic Bilbao",
"a": "Betis Sevilla",
"odds": {
"bothToScore": [{
"name": "no",
"value": 1.85,
"id": 552240303
},
{
"name": "yes",
"value": 1.95,
"id": 552240338
}],
"doubleChance": [{
"name": "12",
"value": 1.22,
"id": 552240012
},
{
"name": "x2",
"value": 2.98,
"id": 552240003
},
{
"name": "1x",
"value": 1.11,
"id": 552240079
}],
"drawNoBet": [{
"name": "1",
"value": 1.15,
"id": 552240007
},
{
"name": "2",
"value": 6.15,
"id": 552240267
}],
"totalGoals": [{
"name": "under",
"value": 2.15,
"id": 552235662
},
{
"name": "over",
"value": 1.7,
"id": 552235663
}]
}
}];
$scope.matches.push(JSON.parse(JSON.stringify($scope.matches[0])));
}]);
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app' ng-controller="ctrl">
<table>
<thead>
<tr>
<th>Date</th>
<th>Pair</th>
<th ng-repeat-start='(key, value) in matches[0].odds' hidden></th>
<th ng-repeat-end ng-repeat='item in value'>{{item.name}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat='match in matches'>
<td>{{match.sd | date}}</td>
<td>{{match.h}} - {{match.a}}</td>
<td ng-repeat-start='(key, value) in match.odds' hidden></td>
<td ng-repeat-end ng-repeat='item in value'>{{item.value}}</td>
</tr>
</tbody>
</table>
</div>

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>

Show table depending on json response

I have a json data with arrays of parameters like Sales, Tax etc. On click of a particular parameter from one page, the landing page should show the table for that particular parameter.
{
"Sales": [
{
"Date": "2015-08-01 23:35:15.652",
"Val": 78
},
{
"Date": "2015-08-01 22:35:15.652",
"Val": 82
},
{
"Date": "2015-08-01 21:35:15.652",
"Val": 80
},
{
"Date": "2015-08-01 21:15:15.652",
"Val": 100
},
{
"Date": "2015-07-27 12:57:15.652",
"Val": 94
}
],
"Tax": [
{
"Date": "2015-08-01 23:35:15.652",
"Min": 78,
"Max":150
},
{
"Date": "2015-08-01 22:35:15.652",
"Min": 50,
"Max":120
},
{
"Date": "2015-08-01 21:35:15.652",
"Min": 65,
"Max":150
},
{
"Date": "2015-08-01 21:25:15.652",
"Min": 70,
"Max":190
},
{
"createdOn": "2015-08-01 21:15:15.652",
"Min": 90,
"Max":200
}
]
}
My controller
angular.module('starter.controllers', [])
.controller('TableCtrl','$scope', '$http', function($scope, $http) {
$http.get('js/data.json').success(function(response){
if(response.data.Sales!=null){
$scope.data =response.data.Sales;
}
if(response.data.Tax!=null){
$scope.data =response.data.Tax;
}
});
});
How do I show table dynamically from the data? Since Sales table will contain 2 columns and Tax table will contain 3 columns.
Table for Sales
<table ng-controller="TableCtrl">
<thead>
<th>
<td>Date</td>
<td>Value</td>
</th>
</thead>
<tbody>
<tr ng-repeat="item in data">
<td>{{item.Date}}</td>
<td>{{item.Val}}</td>
</tr>
</tbody>
</table>
Table for Tax
<table ng-controller="TableCtrl">
<thead>
<th>
<td>Date</td>
<td>Min</td>
<td>Max</td>
</th>
</thead>
<tbody>
<tr ng-repeat="item in data">
<td>{{item.Date}}</td>
<td>{{item.Min}}</td>
<td>{{item.Max}}</td>
</tr>
</tbody>
</table>
How to display different tables based on condition using same Controller, TableCtrl?
In your controller, separate data into $scope.sales and $scope.tax:
if(response.data.Sales != null){
$scope.sales = response.data.Sales;
$scope.tax = null;
}
if(response.data.Tax != null){
$scope.tax = response.data.Tax;
$scope.sales = null;
}
Then, in html use ng-if directive:
<div ng-controller="TableCtrl">
<table ng-if = "sales">
<thead>
<th>
<td>Date</td>
<td>Value</td>
</th>
</thead>
<tbody>
<tr ng-repeat="item in sales">
<td>{{item.Date}}</td>
<td>{{item.Val}}</td>
</tr>
</tbody>
</table>
<table ng-if = "tax">
<thead>
<th>
<td>Date</td>
<td>Min</td>
<td>Max</td>
</th>
</thead>
<tbody>
<tr ng-repeat="item in tax">
<td>{{item.Date}}</td>
<td>{{item.Min}}</td>
<td>{{item.Max}}</td>
</tr>
</tbody>
</table>
</div>
If you have a set of fixed types such as Sales, Tax etc., the hard coded version like #Majid provided is fine. However, you could make a more dynamic version where the table adjusts to the json arrays provided.
Consider the following data:
var data={
"Sales": [
{
"Date": "2015-08-01 23:35:15.652",
"Val": 78
},
{
"Date": "2015-08-01 22:35:15.652",
"Val": 82
},
{
"Date": "2015-08-01 21:35:15.652",
"Val": 80
},
{
"Date": "2015-08-01 21:15:15.652",
"Val": 100
},
{
"Date": "2015-07-27 12:57:15.652",
"Val": 94
}
],
"Tax": [
{
"Date": "2015-08-01 23:35:15.652",
"Min": 78,
"Max":150
},
{
"Date": "2015-08-01 22:35:15.652",
"Min": 50,
"Max":120
},
{
"Date": "2015-08-01 21:35:15.652",
"Min": 65,
"Max":150
},
{
"Date": "2015-08-01 21:25:15.652",
"Min": 70,
"Max":190
},
],
"Inventory": [
{
"Type": "Car",
"Amount": 100,
},
{
"Type": "Bike",
"Amount": 32,
},
{
"Type": "Shoes",
"Amount": 677,
},
]
};
Controller:
function mainCtrl() {
this.data=data;
}
angular
.module('app', [])
.controller('mainCtrl', mainCtrl);
The below html code will now use the first object in each array to print out the property names, and then print each object from the array into to the table accordingly.
<body ng-controller="mainCtrl as main">
<div ng-repeat="(name, table) in main.data">
<h3>{{name}}</h3>
<table>
<thead>
<tr>
<!-- Iterate and print field names -->
<th ng-repeat="(key, value) in table[0]">{{key}}</th>
</tr>
</thead>
<tbody>
<!-- Print rows -->
<tr ng-repeat="row in table">
<td ng-repeat="value in row">{{value}}</td>
</tr>
</tbody>
</table>
</div>
</body>
Live example here: http://jsbin.com/pafanuwoka/edit?html,js,output
Having this solution you need to make sure that all objects in each array are alike as we are printing the field names from the first object in each array. But you are also able to add more json arrays into data field.
If this is a common occurrence in your application, you should probably consider adding this as a directive. Example here: http://jsbin.com/gayirinifo/edit?html,js,output

Warning: Requested unknown parameter '0' for row 0

I have the following code:
var dummyData = {
"activities": [
{ "date": "19/06/2015 19:00", "user": "Dan", "display": "First Item" },
{ "date": "19/06/2015 19:00", "user": "Andrew", "display": "Second Item" },
{ "date": "19/06/2015 19:00", "user": "Trevor", "display": "Third Item" },
{ "date": "19/06/2015 19:00", "user": "Bob", "display": "Fourth Item" }
]
};
$("#sysActTable").dataTable({
"data": dummyData.activities,
});
I have tried several variations but I am always getting the error that points to this page. My HTML is as follows:
<table id="sysActTable" class="table table-hover" style="margin-bottom:0px">
<thead>
<tr>
<th style="width:20%">Date</th>
<th style="width:30%">User</th>
<th style="width:50%">Display</th>
</tr>
</thead>
<tbody>
<tr>
<td style="width:20%"></td>
<td style="width:30%"></td>
<td style="width:50%"></td>
</tr>
</tbody>
again I have tried a number of varieties. Why am I getting this error below?
DataTables warning: table id=sysActTable - Requested unknown parameter '0' for row 0
The problem is that your data is an array of objects. By default, DataTables expects data to be an array of arrays.
You need to use columns.data option to describe your data structure.
$("#sysActTable").dataTable({
"data": dummyData.activities,
"columns": [
{ "data": "date" },
{ "data": "user" },
{ "data": "display" }
]
});

Categories

Resources