How to convert the following table to JSON with Cypress? - javascript

I have the following table and want to convert it into JSON in a specific way
<table>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>A1</td>
<td>A2</td>
<td>A3</td>
</tr>
<tr>
<td>B1</td>
<td>B2</td>
<td>B3</td>
</tr>
<tr>
<td>C1</td>
<td>C2</td>
<td>C3</td>
</tr>
</tbody>
</table>
My expected output would be like this as I need to compare the data over UI in html table and a json file having expected data (Note: there may be a varying number of rows):
{
"myrows" : [
{
"Column 1" : "A1",
"Column 2" : "A2",
"Column 3" : "A3"
},
{
"Column 1" : "B1",
"Column 2" : "B2",
"Column 3" : "B3"
},
{
"Column 1" : "C1",
"Column 2" : "C2",
"Column 3" : "C3"
}
]
}
How can this be accomplished?

It's fairly straight forward to loop over rows then cells in the row.
Use index values from each() command to give you access to the expected value in the JSON.
const expected = { "myrows" : [
... // as in the question, abreviated here
]}
it('check the table', () => {
cy.get('table tbody tr').each(($row, rowIndex) => {
cy.wrap($row).find('td').each(($cell, cellIndex) => {
const text = $cell.text()
expect(text).to.eq(expected.myrows[rowIndex][`Column ${cellIndex}`]
})
})
})

const rows = document.querySelectorAll("tbody tr");
const result = {
myrows: []
};
rows.forEach(r => {
const rData = {}
r.querySelectorAll("td").forEach((c, i) => {
rData[`column_${i + 1}`] = c.innerHTML;
})
result.myrows.push(rData)
})
console.log(result);
<table>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>A1</td>
<td>A2</td>
<td>A3</td>
</tr>
<tr>
<td>B1</td>
<td>B2</td>
<td>B3</td>
</tr>
<tr>
<td>C1</td>
<td>C2</td>
<td>C3</td>
</tr>
</tbody>
</table>

following approach I have followed, taking reference from answer given by "Genevieve OR" on my post, Thanks to #Genevieve OR
var headerList = []
cy.get('table thead tr th').each(($el)=>{
var headerName = $el.text()
headerList.push(headerName)// dynamically make a list of header
)}.then(()=>{
var jsonData = [];
cy.get('table tbody').find('tr').each(($row, rowIndex) => {
jsonData[rowIndex] = {};// creates object for each row
cy.wrap($row).find('td').each(($cell, cellIndex) => {
const text = $cell.text()
jsonData[rowIndex][headerList[cellIndex]] = text
const expected = { "myrows" :jsonData}
})
})
})
My output
{
"myrows": [
{
"column_1": "A1",
"column_2": "A2",
"column_3": "A3"
},
{
"column_1": "B1",
"column_2": "B2",
"column_3": "B3"
},
{
"column_1": "C1",
"column_2": "C2",
"column_3": "C3"
}
]
}

Related

Reorder or create new array based on object indexes

I need to render the below object into subsequent tr/tds via JSX...
{
"0": [
{
"colIndex": 0,
"data": {
"richTextModule": "Data row 1 cell 1"
}
},
{
"colIndex": 0,
"data": {
"richTextModule": "Data row 2 cell 1"
}
}
],
"1": [
{
"colIndex": 1,
"data": {
"richTextModule": "Data row 1 cell 2"
}
},
{
"colIndex": 1,
"data": {
"richTextModule": "Data row 2 cell 2"
}
}
],
"2": [
{
"colIndex": 2,
"data": {
"richTextModule": "Data row 1 cell 3"
}
},
{
"colIndex": 2,
"data": {
"richTextModule": "Data row 2 cell 3"
}
},
{
"colIndex": 2,
"data": {
"richTextModule": "Data row 3 cell 3"
}
}
]
}
I've tried
Object.values(obj).map((column, index) => {
return column.map((row, rowIndex) => {
return obj[index][rowIndex].data.richTextModule;
});
}
Expected output should be...
<tr>
<td>Data row 1 cell 1</td>
<td>Data row 1 cell 2</td>
<td>Data row 1 cell 3</td>
</tr>
<tr>
<td>Data row 2 cell 1</td>
<td>Data row 2 cell 2</td>
<td>Data row 2 cell 3</td>
</tr>
Any ideas on how to achieve this?
Think what you're trying to do is:
<table>
{Object.values(obj).map((col, index) => <tr key={index}>{
col.map((row, rowIndex) => <td key={rowIndex}>${obj[index][rowIndex].data.richTextModule}</td>)
}</tr>
)}
</table>
or you could do:
<table>
{Object.values(obj).map((col, index) => {
return (
<tr key={index}>
{col.map((row, rowIndex) => {
return <td key={rowIndex}>${obj[index][rowIndex].data.richTextModule}</td>
})}
</tr>
)
})}
</table>
You should return in a map, reference:
Rendering an array.map() in React
You also should add a key, reference:
Lists and Keys

Rearrage html table column in jquery

HTML table
<thead>
<tr>
<th class="text-center">1</th>
<th class="text-center">3</th>
<th class="text-center">2</th>
</tr>
</thead>
<tbody class="result_body">
</tbody>
data
var data = [
{
"0": "1",
"1": "20"
},
{
"0": "2",
"1": "30"
},
{
"0": "3",
"1": "5"
}
]
I create one table with th name 1,3,2, after that inside jquery I try to loop the data and append into the table. But it will follow the sequence with is 1,2,3. How can I append the data follow the table sequence with is 1,3,2?
Jquery Data output
<th>
<td>20</td>
<td>30</td>
<td>5</td>
<th>
Data correct should be
<th>
<td>20</td>
<td>5</td>
<td>30</td>
<th>
code here
https://jsfiddle.net/vwL0frhm/1/
Iterate your headers, and find the corresponding value (instead to iterate the array):
$('#opt_head th').each(function(dkey, dvalue) {
let textHeader = $(dvalue).text();
let value = data.find(o => o[0] === textHeader)[1];
html += "<td>"+ value +"</td>";
});
jsfiddle: https://jsfiddle.net/hansfelix50/Ltqvr3bm/

Javascript put data on <template> and append it on tbody after axios request

I have here a table in which a person's data will be displayed
<table class="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Username</th>
<th scope="col">Birthdate</th>
<th scope="col">Age</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<!-- existing data could optionally be included here -->
</tbody>
</table>
this template is will be use on putting the data and append it on the tbody
<template id="persons">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</template>
this is the JavaScript code that I have
let oCRUD = {
init: function() {
this.setDOMElements();
this.getPersons();
},
// Setting DOM Elements
setDOMElements: function() {
this.oTemplate = document.querySelector('#persons'); //persons row
this.oTbody = document.querySelector("tbody");
this.oClone = document.importNode(oCRUD.oTemplate.content, true);
this.oTd = oCRUD.oClone.querySelectorAll("td");
},
getPersons: function() {
axios.get('selectAll.php')
.then(function (response) {
response.data.forEach((element,index) => {
oCRUD.oTd[0].textContent = element.name;
oCRUD.oTd[1].textContent = element.username;
oCRUD.oTd[2].textContent = element.birthdate;
oCRUD.oTd[3].textContent = element.age;
oCRUD.oTd[4].textContent = element.email;
oCRUD.oTbody.appendChild(oCRUD.oClone);
});
})
.catch(function (error) {
console.log(error);
});
}
}
// call the init function
oCRUD.init();
How can I use the template put the data there after the successful response of axios and append it on the tbody. This is my first time using DOM templating I have no idea how to start it.
This is the successful response after axios get request
[
{
id: "1",
name: "john",
username: "john doe",
birthdate: "1999-05-21",
age: "20",
email: "test#gmail.com",
},
{
id: "2",
name: "sally",
username: "sally mcsalad",
birthdate: "1999-03-27",
age: "20",
email: "try#gmail.com",
},
]
EDIT: I SUCCESSFULLY SHOW THE DATA HOWEVER I ONLY GOT THE SECOND SET OF DATA (sally mcsalad) NOT THE WHOLE DATA
Your main problem is you only clone the node, and select the tds once. This counts as a single object, which will just update the existing elements on each iteration. You need to refresh the clone and the selected tds on each iteration
var data = [
{
id: "1",
name: "john",
username: "john doe",
birthdate: "1999-05-21",
age: "20",
email: "test#gmail.com",
},
{
id: "2",
name: "sally",
username: "sally mcsalad",
birthdate: "1999-03-27",
age: "20",
email: "try#gmail.com",
},
];
let oCRUD = {
init: function() {
this.setDOMElements();
this.getPersons();
},
// Setting DOM Elements
setDOMElements: function() {
this.oTemplate = document.querySelector('#persons'); //persons row
this.oTbody = document.querySelector("tbody");
this.oClone = document.importNode(oCRUD.oTemplate.content, true);
this.oTd = oCRUD.oClone.querySelectorAll("td");
},
refreshClone: function() {
this.oClone = document.importNode(oCRUD.oTemplate.content, true);
this.oTd = oCRUD.oClone.querySelectorAll("td");
},
getPersons: function() {
/*axios.get('selectAll.php')
.then(function (response) {*/
data.forEach((element,index) => {
oCRUD.refreshClone();
oCRUD.oTd[0].textContent = element.name;
oCRUD.oTd[1].textContent = element.username;
oCRUD.oTd[2].textContent = element.birthdate;
oCRUD.oTd[3].textContent = element.age;
oCRUD.oTd[4].textContent = element.email;
oCRUD.oTbody.appendChild(oCRUD.oClone);
});
/*})
.catch(function (error) {
console.log(error);
});*/
}
}
// call the init function
oCRUD.init();
<table class="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Username</th>
<th scope="col">Birthdate</th>
<th scope="col">Age</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<!-- existing data could optionally be included here -->
</tbody>
</table>
<template id="persons">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</template>
add the function generateTable to your code and call it after success your request
function generateTable(persons){
let oTbody = document.querySelector("tbody");
if(!persons) return;
persons.forEach( person=>{
let tr =
`<tr id=${person.id}>
<td>${person.name}</td>
<td>${person.username}</td>
<td>${person.birthday}</td>
<td>${person.age}</td>
<td>${person.email}</td>
</tr>`
oTbody.insertAdjacentHTML('beforeend', tr);
})
}
let persons = [
{
id: "1",
name: "john",
username: "john doe",
birthdate: "1999-05-21",
age: "20",
email: "test#gmail.com",
},
{
id: "2",
name: "sally",
username: "sally mcsalad",
birthdate: "1999-03-27",
age: "20",
email: "try#gmail.com",
},
]
generateTable(persons)
<table class="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Username</th>
<th scope="col">Birthdate</th>
<th scope="col">Age</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<!-- existing data could optionally be included here -->
</tbody>
</table>
Use template function in general, such as lodash.template or jQuery.tmpl, you can also implement a simple template function.
Step1: define a template function to transit each data object to html string.
# use ES2015 string template feature
function transitData(data){
return '<tr>' +
`<td class="record">${data.name}</td>` +
`<td>${data.email}</td>` +
'</tr>';
}
Step2: get Server response and render your data collection(such as array).
axios.get('selectAll.php').then(response => {
let html = response.data.map(transitData).join("\n");
oTbody.insertAdjacentHTML('beforeend', html);
});

Display data in a table by grouping them horizontally

I have some data that has the following format:
[name:'Name1', speed:'Val1', color:'Val2']
[name:'Name2', speed:'Val4', color:'Val5']
[name:'Name3', speed:'Val6', color:'Val7']
That I want to display in a table like this:
|Name1|Name2|Name3|
______|_____|______
speed |Val1 |Val4 |Val6
color |Val2 |Val5 |Val7
What I tried to do is group my data like this in the controller:
$scope.data = {
speeds: [{
...
},{
...
},{
...
}],
colors: [{
...
},{
...
},{
...
}],
};
But I am not sure what to put inside the empty areas, because all values there represent the values of the 'val1' variable for all Names (Accounts), and my tests until now keep failing.
You can imagine this as some sort of a comparisons matrix, that is used in order to see the all the values of the same variable across different accounts.
How can I represent the data in my model in order for me to successfully display them in a table as explained?
Edit
My difficulty lies in the fact that you create a table by going from row to row, so my html looks something like this:
<table md-data-table class="md-primary" md-progress="deferred">
<thead>
<tr>
<th ng-repeat="header in headers">
{{header.value}}
</th>
</tr>
</thead>
<tbody>
<tr md-auto-select ng-repeat="field in data">
<td ng-repeat="var in field">{{var.value}}</td>
</tr>
</tbody>
</table>
So as you can see I have a loop for each row, and a loop for each value of each row. This would be easier if I wanted to display the data horizontally, but I want the data vertically. So if we where talking about cars, we would have the car models as headers, and their respective characteristics(speed, color, etc) in each row.
If this is your basic structure:
var cols = [{name:'Name1', val1:'Val1', val2:'Val2'},
{name:'Name2', val1:'Val4', val2:'Val5'},
{name:'Name3', val1:'Val6', val2:'Val7'}];
This code
$scope.table = cols.reduce(function(rows, col) {
rows.headers.push({ value: col.name });
rows.data[0].push({ value: col.speed });
rows.data[1].push({ value: col.color });
return rows;
}, {headers:[], data:[[], []]});
will give you this structure for $scope.table:
$scope.table = {
headers : [{
value : "Name1"
}, {
value : "Name2"
}, {
value : "Name3"
}
],
data : [
[{
value : 'val1'
}, {
value : 'val4'
}, {
value : 'val6'
}
],
[{
value : 'val2'
}, {
value : 'val5'
}, {
value : 'val17'
}
]
]
};
<table md-data-table class="md-primary" md-progress="deferred">
<thead>
<tr>
<th ng-repeat="header in table.headers">
{{header.value}}
</th>
</tr>
</thead>
<tbody>
<tr md-auto-select ng-repeat="field in table.data">
<td ng-repeat="var in field">{{var.value}}</td>
</tr>
</tbody>
</table>
You could try this:
HTML
<table ng-app="myTable" ng-controller="myTableCtrl">
<thead>
<tr>
<th ng-repeat="car in cars">{{car.name}}</th>
</tr>
</thead>
<tbody>
<tr>
<td ng-repeat="car in cars">{{car.speed}}</td>
</tr>
<tr>
<td ng-repeat="car in cars">{{car.color}}</td>
</tr>
</tbody>
</table>
JS
angular.module("myTable",[])
.controller("myTableCtrl", function($scope) {
$scope.cars = [
{
name:'Name1',
speed:'Val1',
color:'Val2'
},
{
name:'Name2',
speed:'Val4',
color:'Val5'
},
{
name:'Name3',
speed:'Val6',
color:'Val7'
}
]
});
https://jsfiddle.net/ABr/ms91jezr/

How to sort capacities column in Datatables

i have a datatable with capacities column like this :
<table id="datatable" class="table">
<thead> <tr> <th>N</th> <th>capa</th> </tr> </thead>
<tbody>
<tr>
<td>1</td>
<td>2 Go</td>
</tr>
<tr>
<td>2</td>
<td>1 To</td>
</tr>
<tr>
<td>3</td>
<td>320 Go</td>
</tr>
<tr>
<td>4</td>
<td>2 To</td>
</tr>
<tr>
<td>5</td>
<td>500 Go</td>
</tr>
</tbody>
</table>
<script>
$(document).ready(function() {
$('#datatable').dataTable({
'aaSorting': [],
'iDisplayLength': 50,
'aLengthMenu': [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'Tous']]
});
});
</script>
and I am trying to sort it to get this result :
2 Go
320 Go
500 Go
1 To
2 To
But can't figure out how to do it from reading the sorting plugins docs.
Thanks
Ok, finally got it
http://jsfiddle.net/jkwoaj3x/1/
$('#datatable').dataTable({
"columns": [
null,
{ "orderDataType": "custom-sort" }
]
});
and this is your custom sort func
$.fn.dataTable.ext.order['custom-sort'] = function ( settings, col )
{
return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
console.log($(td).text().replace(/[^0-9\.]+/g, ''));
return $(td).text().replace(/[0-9]/g, '');
} );
}
is it your solution?
If I understand correctly, you want to sort on the text part of the 'capa' column. You can achieve this by adding a column containing the text field, hiding it, and using iDataSort to sort on the hidden column when the 'capa' column header is clicked.
First, add the new text-only column to each row:
<tr>
<td>1</td>
<td>2 Go</td>
<td>Go</td>
</tr>
In the datatable initialisation code, use aoColumns to specify the column definitions:
...
'iDisplayLength': 50,
'aoColumns': [{},{ "iDataSort": 2 }, {'bVisible':false }],
...
Here's a working jsfiddle
Update: so it sounds like you want to sort on the text column THEN the int column, it would have been helpful if you had just stated that earlier.
'aoColumns': [{},{ "aDataSort": [2], "aTargets": [ 0, 2 ] }, {'bVisible': false, "aTargets": [ 0 ] }],
Here's an updated jsfiddle
you can have a table with all source data in gigas but render it differently without change nested data thanks to render in columnDefs option , it will use the built-in sort for numbers that works very well
http://legacy.datatables.net/usage/columns
I always do that when i want to display sentences and still have sortable column and it is very efficient
<table id="datatable" class="table">
<thead> <tr> <th>N</th> <th>capa</th> </tr> </thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>2</td>
<td>1000 </td>
</tr>
<tr>
<td>3</td>
<td>320</td>
</tr>
<tr>
<td>4</td>
<td>2000</td>
</tr>
<tr>
<td>5</td>
<td>500</td>
</tr>
</tbody>
</table>
//targets is the number of the column you want render (here number 1)
//care full!!!! use DataTable and not datatable, second is old and doesn't have all options, if you don't want use fnRender
table = $('#datatable').DataTable({
"columnDefs":{
"targets": 1, "visible": true, "searchable": true
, "render": function (data, type, row) {
if (type == "display") {
if (data > 1000)
return ((data / 1000) + " To");
else
return (data + " Go");
}
return data;
},
};
});
It is best solution !
Thanks everyone.
I'm putting my answer which works well for me.
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
"file-size-pre": function (a) {
var x = a.substring(0, a.length - 2);
var x_unit = (a.substring(a.length - 2, a.length) == "Go" ? 1000 : (a.substring(a.length - 2, a.length) == "To" ? 1000000 : 1));
return parseInt(x * x_unit, 10);
},
"file-size-asc": function (a, b) {
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
},
"file-size-desc": function (a, b) {
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
}
});
$(document).ready(function () {
$('#datatable').dataTable({
'columnDefs': [{ 'type': 'file-size', 'targets': 1 }],
'aaSorting': [],
'iDisplayLength': 50,
'aLengthMenu': [ [10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'Tous'] ]
});
});
Here is a fiddle : http://jsfiddle.net/cu9taqfg/1/

Categories