Problem
I am making a Meteor.js app, and are importing .csv files via Papaparse.js, which I save in a Mongo collection for later use in a table. The data is saved in a standard Papaparse.js structure:
results = {
data: [ ... ], // parsed data
errors: [ ... ], // errors encountered
meta: { ... } // extra parse info
}
The data array looks like this (from Chrome console):
image
I output this in a front-end view, where this data is shown as a table. But sometimes, the data gets all unsorted. This often happens if I move the DB to another server or import the DB to local. Look at this image for a comparison: image
The question is why, and how I can fix it.
This is my table template:
(note that projectData.data is from Iron Router, which is the Papaparse data (results.data))
<template name="table">
<div class="table-responsive">
<table class="table table-striped table-hover sortable-theme-bootstrap table-fixedheader" data-sortable>
<thead>
<tr>
{{#each addStatusColToArray projectData.meta.fields}}
<th>{{this}}</th>
{{/each}}
<th class="data-controls-header"></th>
</tr>
</thead>
<tbody>
{{#each projectData.data}}
<tr>
{{#each tableObjectToKeyPairs this}}
<td class="{{key}}">
{{value}}
</td>
{{/each}}
<td class="data-controls">
<span class="glyphicon glyphicon-menu-right" aria-hidden="true"></span>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</template>
And this is the corresponding JS file:
Template.embedTable.helpers({
rowClassPropId: function (parentContext) {
var colLink = parentContext.projectData.meta.colLink;
var propId = this[colLink];
return propId;
},
tableObjectToKeyPairs: function (object) {
var filteredObject = _.omit(object, "_id"); // Omit the "_id" value
return _.map(filteredObject, function (value, key) {
return {
key: key,
value: value
};
});
}
});
Template.registerHelper("addStatusColToArray", function (array) {
if (array) {
array.push("Status"); // Pushes the "Status" last into array
return array; // Returns the modified array
}
});
Related
I have a data table which displays 'Name' field and 'Address' field (Address related to another object). The 'Name' field is displayed properly but if I iterate over the list in html for pcon.Address__r.Name it gives error. If only pcon.Address__r is given , it shows [object object] in table. Can anybody resolve this issue?
HTML:
<table class="slds-table slds-table--bordered">
<thead>
<tr>
<th style="background-color: #151963;color: white;">Name</th>
<th style="background-color: #151963;color: white;">Address</th>
</tr>
</thead>
<tbody>
<template for:each={pconfig} for:item="pcon" for:index="i">
<tr class={pcon.row} data-id={pcon.Id} data-index={i} key={pcon.Id} onclick{getquotes} >
<td>{pcon.Name}</td>
<td>{pcon.Address__r.Name}</td>
</tr>
</template>
</tbody>
</table>
</lightning-layout-item>
JS:
#wire(Vendor_getProduct,{ basketId: 'fef0E0000dwedjh' })
getProducts({error,data})
{
if(data)
{
//console.log( 'Fetched Data ' + JSON.stringify( data ) );
this.pconfig = JSON.parse( JSON.stringify( data) );
console.log('products are:',data);
}
else if(error){
this.error=data;
console.log('error is undefined');
}
}
class:
public class c {
#AuraEnabled(cacheable=true)
public static List<Product_Configuration__c> getProduct (Id basketId) {
return [select id,
name,
Address__r.name,
from Product_Configuration__c
where Product_Basket__c =:basketId];
}
I have a search box which I wish to use to populate my table so i want to access the data sent to the view template in the script tag
ERROR IN SCRIPT
1 Uncaught SyntaxError: "[object Object]" is not valid JSON
at JSON.parse (<anonymous>)
at 1:164:25
<div class="FieldElement2" style="margin-top: 3%;">
<div class="input-field ">
<input id="search_masterTable" type="text" class="validate">
</div>
<table class="highlight centered responsive-table" style="width: min-content;" >
<thead>
<tr>
<th></th>
<th>Ledg.No.</th>
<th>File No.</th>
<th>Name</th>
<th>Phone</th>
</tr>
</thead>
<tbody id="searchResultsBody">
{{#each accounts_array}}
<tr>
<td><button class="viewButton">View Chart</button></td>
<td class="Ledg_No" scope="row">{{ledger_num}}</td>
<td class="File_Num" scope="row">{{file_num}}</td>
<td class="Name">{{client_name}}</td>
<td class="gPhoneNumber3">{{phone_number6}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<script>
document.getElementById('search_masterTable').addEventListener('input',()=>{
const search_value = document.getElementById('search_masterTable').value.toUpperCase().split(' ')
// NOT WOKING
const data = JSON.parse("{{accounts_array}}")
const result_array = data.filter(function(r){
return search_value.every(function(word){
console.log(word)
return column_name_array.some(function(colIndex){
console.log(r[colIndex])
if(r[colIndex] == null){
return false
}else{
return r[colIndex].toString().toUpperCase().indexOf(word) !== -1
}
});
});
})
function to_populate_masterTable(){
}
</script>
insted of sending the search value back to the server i want to perfom the search opearation in the frontend and send display the result for every search input
BACK END
router.get('/masterTable/:id', ensureAuth, async (req, res) => {
const title = 'Master Table'
var account = parseInt(req.params.id)
db.query(`SELECT * FROM mt_`+account+``, async (err,accounts_array) => {
res.render('masterTable', {
accounts_array,
title
})
})
})
THIS IS WHAT THE account_array LOOKS LIKE
[
RowDataPacket { // <= this is the error
id: 1,
ledger_num: 'VM364',
file_num: 'VM364',
client_name: 'MUTHU KUMAR',
phone_number1: '9791716460',
status: 'UNSETTLED'
},
RowDataPacket {
id: 1,
.,
.,
.,
},
RowDataPacket {},
RowDataPacket {}
]
error that i got
Uncaught SyntaxError: Unexpected token '{' (at 1:169:21)
This might work by first stringifying the variable and using the tag syntax to pass the stringified JSON to the template:
const data = JSON.parse(<%= JSON.stringify(accounts_array) %>);
In following table there can be more than one list on particular date. So For list column there can be more than one list for given date.
I am able to insert more than one value in single cell but it shift the row in which I inserted more than one value, Please look at DEMO.
Example: Table
Date...............List
12/1/2016 .... python, angularjs
13/1/2016..... java, html
data:
$scope.todolists = [{
date: '12/1/2016',
list: {python, angularjs}
}, {
date: '13/1/2016',
list: {java, html}
}];
view:
<tbody>
<tr ng-repeat="todolist in todolists" >
<td>{{todolist.date}}</td>
<td ng-repeat="list in todolist">{{subject}}</td>
</tr>
</tbody>
I tried ng-repeat inside ng-repeat but it is not working. So my question is how to insert more than one value in single cell in table.
Your references are wrong.
<tbody>
<tr ng-repeat="todolist in todolists" >
<td>{{todolist.date}}</td>
<td ng-repeat="list in todolist.list">{{list}}</td>
</tr>
</tbody>
or, preferably,
<tbody>
<tr ng-repeat="todolist in todolists" >
<td>{{todolist.date}}</td>
<td>
<span ng-repeat="list in todolist.list">
{{list + ($last ? "" : ", ") }}
</span>
</td>
</tr>
</tbody>
Your JSON is invalid. If you want to store values without properties, you need to use Array but not Object. Also, if the values are string you need to wrap them with comma like: ['val1', 'val2', ...];
You can do ng-repeat inside ng-repeat but you need to iterate the right property todolist.list.
You can't use {{subject}} if you have not property subject in you model. So you need to use {{list}} when you do the ng-repeat like ng-repeat="list in todolist.list".
The full code:
angular.module('app', []).
controller('ctrl', function($scope) {
$scope.todolists = [{ date: '12/1/2016', list: ['python', 'angularjs'] }, { date: '13/1/2016', list: ['java', 'html'] }];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<table>
<tr ng-repeat="todolist in todolists">
<td>{{todolist.date}}</td>
<td ng-repeat="list in todolist.list">{{list}}</td>
</tr>
</table>
</div>
Your datas are not properly declared. It should be:
$scope.todolists = [{
date: '12/1/2016',
list: [
'python',
'angularjs'
]
}, {
date: '13/1/2016',
list: [
'java',
'html'
]
}];
Note the '' around python, etc. and the list should be a list => [] and not {}.
A nice way to go is to write a custom filter:
angular.module("myApp", [])
.filter('nicelist', function() {
return function(input) {
if (input instanceof Array) {
return input.join(",");
}
return input;
}
});
Then, you can use :
<table>
<tr ng-repeat="todolist in todolists">
<td>{{todolist.date}}</td>
<td>{{todolist.list | nicelist}}</td>
</tr>
</table>
Here is a working fiddle.
In my angular app I'm trying to display JSON data in a table. The data looks like this:
$scope.data =
{
"EVENT NAME":"Free Event",
"ORDER ID":311575707,
"DATE":"6/26/14",
"GROSS REVENUE (USD)":"0",
"TICKET REVENUE (USD)":"0",
"EVENTBRITE FEES (USD)":"0",
"CC PROCESSING (USD)":"0",
"TICKETS":1,
"TYPE":"Free Order",
"STATUS":"Free Order",
"TRANSACTION ID":"",
"NOTES":"",
"FIRST NAME":"Khee Seng",
"LAST NAME":"Chua",
"EMAIL ADDRESS":"email#anemailadderss.com"
};
And I'm displaying it like this:
<table class="table table-striped selector">
<tbody>
<tr>
<td ng-repeat="(key, value) in data">
<strong>{{key}}</strong>
</td>
</tr>
<tr>
<td ng-repeat="(key, value) in data">
{{value}}
</td>
</tr>
</tbody>
</table>
In my mind this should go through each `(key, value) pair in the object and display it in order. However, AngularJS displays the values in alphabetical order.
Here's a plunkr which replicates this issue: http://plnkr.co/edit/V3Y2ZuwV1v9Pzsl0jGhA?p=preview
How can I tweak the code so it displays in the natural order that the object actually comes in?
You can achieve it like this
Working Demo
In the scope define a method like as shown
$scope.notSorted = function(obj){
if (!obj) {
return [];
}
return Object.keys(obj);
}
and in html like as shown below
html
<table class="table table-striped selector">
<tbody>
<tr>
<th ng-repeat="key in notSorted(data)">
{{key}}
</th>
</tr>
<tr>
<td ng-repeat="key in notSorted(data)" ng-init="value = data[key]">
{{value}}
</td>
</tr>
</tbody>
</table>
Original Article: ng-repeat with no sort? How?
A Javascript object does not have the concept of 'natural order' of its keys:
Definition of an Object from ECMAScript Third Edition (here):
4.3.3 Object
An object is a member of the type Object. It is an unordered collection of properties
each of which contains a primitive value, object, or function [...]
You probably should change a bit your data structure...
For example:
$scope.data =
{
1: { "EVENT NAME": "Free Event" },
2: { "ORDER ID": 311575707 },
/* ... */
};
And then use the numerical key to sort your items...
Object properties don't have a natural order.
You can achieve what you're looking for with a slightly different Object:
$scope.data =
{
columns: [
{
"EVENT NAME":"Free Event",
"priority": 0
},
{
"ORDER_ID":311575707,
"priority": 1
},
...
]
}
I'm looking for a way to add rows to a table. My data structure looks like that:
rows = [
{ name : 'row1', subrows : [{ name : 'row1.1' }, { name : 'row1.2' }] },
{ name : 'row2' }
];
I want to create a table which looks like that:
table
row1
row1.1
row1.2
row2
Is that possible with angular js ng-repeat? If not, what would be a "angular" way of doing that?
Edit:
Flatten the array would be a bad solution because if i can iterate over the sub elements i could use different html tags inside the cells, other css classes, etc.
More than one year later but found a workaround, at least for two levels (fathers->sons).
Just repeat tbody's:
<table>
<tbody ng-repeat="row in rows">
<tr>
<th>{{row.name}}</th>
</tr>
<tr ng-repeat="sub in row.subrows">
<td>{{sub.name}}</td>
</tr>
</tbody>
</table>
As far as I know all browsers support multiple tbody elements inside a table.
More than 3 years later, I have been facing the same issue, and before writing down a directive I tried this out, and it worked well for me :
<table>
<tbody>
<tr ng-repeat-start="row in rows">
<td>
{{ row.name }}
</td>
</tr>
<tr ng-repeat-end ng-repeat="subrow in row.subrows">
<td>
{{ subrow.name }}
</td>
</tr>
</tbody>
</table>
You won't be able to do this with ng-repeat. You can do it with a directive, however.
<my-table rows='rows'></my-table>
Fiddle.
myApp.directive('myTable', function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
var html = '<table>';
angular.forEach(scope[attrs.rows], function (row, index) {
html += '<tr><td>' + row.name + '</td></tr>';
if ('subrows' in row) {
angular.forEach(row.subrows, function (subrow, index) {
html += '<tr><td>' + subrow.name + '</td></tr>';
});
}
});
html += '</table>';
element.replaceWith(html)
}
}
});
I'm a bit surprised that so many are advocating custom directives and creating proxy variables being updated by $watch.
Problems like this are the reason that AngularJS filters were made!
From the docs:
A filter formats the value of an expression for display to the user.
We aren't looking to manipulate the data, just format it for display in a different way. So let's make a filter that takes in our rows array, flattens it, and returns the flattened rows.
.filter('flattenRows', function(){
return function(rows) {
var flatten = [];
angular.forEach(rows, function(row){
subrows = row.subrows;
flatten.push(row);
if(subrows){
angular.forEach(subrows, function(subrow){
flatten.push( angular.extend(subrow, {subrow: true}) );
});
}
});
return flatten;
}
})
Now all we need is to add the filter to ngRepeat:
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>Rows with filter</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows | flattenRows">
<td>{{row.name}}</td>
</tr>
</tbody>
</table>
You are now free to combine your table with other filters if desired, like a search.
While the multiple tbody approach is handy, and valid, it will mess up any css that relies on the order or index of child rows, such as a "striped" table and also makes the assumption that you haven't styled your tbody in a way that you don't want repeated.
Here's a plunk: http://embed.plnkr.co/otjeQv7z0rifPusneJ0F/preview
Edit:I added a subrow value and used it in the table to show which rows are subrows, as you indicated a concern for being able to do that.
Yes, it's possible:
Controller:
app.controller('AppController',
[
'$scope',
function($scope) {
$scope.rows = [
{ name : 'row1', subrows : [{ name : 'row1.1' }, { name : 'row1.2' }] },
{ name : 'row2' }
];
}
]
);
HTML:
<table>
<tr ng-repeat="row in rows">
<td>
{{row.name}}
<table ng-show="row.subrows">
<tr ng-repeat="subrow in row.subrows">
<td>{{subrow.name}}</td>
</tr>
</table>
</td>
</tr>
</table>
Plunker
In case you don't want sub-tables, flatten the rows (while annotating subrows, to be able to differentiate):
Controller:
function($scope) {
$scope.rows = [
{ name : 'row1', subrows : [{ name : 'row1.1' }, { name : 'row1.2' }] },
{ name : 'row2' }
];
$scope.flatten = [];
var subrows;
$scope.$watch('rows', function(rows){
var flatten = [];
angular.forEach(rows, function(row){
subrows = row.subrows;
delete row.subrows;
flatten.push(row);
if(subrows){
angular.forEach(subrows, function(subrow){
flatten.push( angular.extend(subrow, {subrow: true}) );
});
}
});
$scope.flatten = flatten;
});
}
HTML:
<table>
<tr ng-repeat="row in flatten">
<td>
{{row.name}}
</td>
</tr>
</table>
Plunker
Here is an example. This code prints all names of all the people within the peopeByCity array.
TS:
export class AppComponent {
peopleByCity = [
{
city: 'Miami',
people: [
{
name: 'John', age: 12
}, {
name: 'Angel', age: 22
}
]
}, {
city: 'Sao Paulo',
people: [
{
name: 'Anderson', age: 35
}, {
name: 'Felipe', age: 36
}
]
}
]
}
HTML:
<div *ngFor="let personsByCity of peopleByCity">
<div *ngFor="let person of personsByCity.people">
{{ person.name }}
</div>
</div>