I am trying to create a table of orders. I work with React+Redux.
I have my data stored in props.
The data is structured similar to this: (a bit more detailed)
[{ //stored in props(redux state)
"id": 37, //order 1
"content": {
"items": {
"47427": {
"price": "12.49",
"format": "[\"1x12\"]",
"quantity": 1,
},
"23451": {
"price": "18.99",
"format": "[\"1x7\"]",
"quantity": 1,
},
}
},
"address": {
"first_name": "Tyrion",
"last_name": "Lannister",
"line1": "The Red Keep",
"city": "King's Landing",
"country": "Westeros",
}
}, {
"id": 38, //order 2
"content": {
"items": {
"183429": {
"price": "8.99",
"format": "[\"1x7\"]",
"quantity": "1",
}
}
},
"address": {
"first_name": "Arya",
"last_name": "Stark",
"line1": "23 Wolf st.",
"city": "Winterfell",
"country": "Westeros",
}
}, {
"id": 30, //order 3
"content": {
"items": {
"20399": {
"price": "21.99",
"format": "[\"1x12\"]",
"quantity": 1,
}
}
},
"address": {
"first_name": "Jon",
"last_name": "Snow",
"line1": "29 Winter is here st.",
"city": "The Wall",
"country": "Westeros",
}
}]
I want to access the "content" and "address" properties of each order and display it in a table. So i tried to call orders.map() on this object but this only gives me access to the first layer of properties - for instance order.id.
When i try to access order.content.item.price i get an error "can't read property of undefined".
My question goes on about order.content.items. that's when i have another object to iterate over since it has different property names, that contain properties inside them (.price, .format, .quantity).
So basically, how do i handle this complex data, so I can grab every piece of info in this object and place them in my table?
//my render function of <OrdersTable />
render() {
let filter = this.props.filter || {};
let orders = this.props.orders || [];
let content = orders.map(order => {
return (
<tr key={order.id}>
<td>{order.content}</td>
</tr>
)
})
let address = orders.map(order => {
return (
<tr key={order.id}>
<td>{order.address.first_name}</td>
</tr>
)
})
return (
<div>
<button className="filter"
onClick={this.props.showContent}>
Show Content
</button>
<button className="filter"
onClick={this.props.showAddress}>
Show Address
</button>
<table className='orders'>
<thead className={filter.showContent?'content': 'content hidden'}>
<tr>
<th>Items</th>
<th>Format</th>
<th>Quantity</th>
<th>Price</th>
</tr>
</thead>
<thead className={filter.showAddress?'address': 'address hidden'}>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Country</th>
<th>City</th>
</tr>
</thead>
<tbody className={filter.showContent?'content': 'content hidden'}>
{content}
</tbody>
<tbody className={filter.showAddress?'address': 'address hidden'}>
{address}
</tbody>
</table>
</div>
)
Thank you very much :)
EDIT:
So here's what did the trick in my case: (with the help of #TW80000 and #TomDavies)
let content = orders.map(order => {
const items= Object.keys(order.content.items).map(id => {
const item= order.content.items[id];
return(
<tr key={id}>
<td>{id}</td>
<td>{item.format}</td>
<td>{item.quantity}</td>
<td>{item.price}</td>
</tr>
)
})
return items;
});
const address = orders.map(order => {
return (
<tr key={order.id}>
<td>{order.address.first_name}</td>
<td>{order.address.last_name}</td>
<td>{order.address.country}</td>
<td>{order.address.state}</td>
<td>{order.address.city}</td>
<td>{order.address.line1}</td>
<td>{order.address.zip}</td>
</tr>
)
});
...
<tbody className={filter.showContent?'content': 'content hidden'}>
{content}
</tbody>
<tbody className={filter.showAddress?'address': 'address hidden'}>
{address}
</tbody>
Since you are working with complex data, I'd rather separate everything into small subcomponents so it get's easier to use and understand the code later.
// Only order details
const OrderRow = (order) => {
return (
<tr>
{
Object.keys(order.content.items).map((key, index) => {
const item = order.content.items[key]
return (
<td key={index}>{item.price} {item.format} {item.quantity}</td>
)
})
}
</tr>
)
}
// Only address details
const AddressRow = (address) => {
return (
<tr>
<td>{adress.first_name}</td>
</tr>
)
}
// In your main component, render them all
render() {
...
return (
<table>
...
<tbody>
{orders.map(order => <OrderRow key={order.id} order={order} />)}
</tbody>
<tbody>
{orders.map(order => <AddressRow key={order.id} address={order.address} />)}
</tbody>
</table>
)
}
I want to access the "content" and "adress" properties of each order and display it in a table.
You've spelled address wrong pretty much everywhere, that could be why it's not being read correctly.
So i tried to call orders.map() on this object but this only gives me access to the first layer of properties - for instance order.id.
That's not true, you will have access to all properties: id, address, and content.
When i try to access order.content.item.price i get an error "can't read property of undefined".
That's because it's content.items with an "s", not content.item like you have written. content.item is undefined and that's why you get this error.
My question goes on about order.content.items. that's when i have another object to iterate over since it has different property names, that contain properties inside them (.price, .format, .quantity).
You can do a nested loop. Example:
let content = orders.map(order => {
const tds = Object.keys(order.content.items).map(id => {
const item = order.content.items[id];
// You can use item.quality and item.format here as well
return(<td>{item.price}</td>);
})
return (
<tr key={order.id}>{tds}</tr>
);
})
Related
I'm trying to generate six different React bootstrap tables based on the groupName field of the below data (more objects inside, just used three for example):
CompetitionData.js
const data= {
"players": [],
"teams": [
{
"id": 1,
"name": "England",
"groupName": 4
},
{
"id": 2,
"name": "Germany",
"groupName": 6
},
{
"id": 4,
"name": "Russia",
"groupName": 2
}
]
}
export default data;
Currently I'm able to map through the data and generate a single table, but not sure how to loop through the data just once in order to generate six different tables...
Standings.js
import React from "react";
import { Table } from "react-bootstrap";
import CompetitionData from "../data/CompetitionData";
export const Standings = () => {
const data = CompetitionData;
return (
<Table striped bordered hover size="sm" variant ="dark">
<thead>
<tr>
<td>Country</td>
<td>Group</td>
</tr>
</thead>
<tbody>
{data?.teams?.length
? data.teams.map((team, index) => (
<tr key={index}>
<td>{team.name}</td>
<td>{team.groupName}</td>
</tr>
))
: null}
</tbody>
</Table>
);
};
Any idea how to go about doing that?
I am using table for showing the datas and for data I am using Api.
Api data looks like::
{
"data": [
{
"id": "1",
"name": "name1",
"label": "label1"
},
{
"id": "2",
"name": "name2",
"label": "label2"
},
{
"id": "3",
"name": "name3",
"label": "label3"
}
]
}
html code
<table class="table table-striped">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Label</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of sample;">
<td>{{data.id}}</td>
<td>{{data.name}}</td>
<td>{{data.label}}</td>
<tr>
<tbody>
<table>
I need the 10 table rows statically(fixed). The table data is from API. For exanple ,Api contains 2 data... Then UI table should be with 2 rows of data and balance with emply rows... but there should display 10 rows(Mandatory)
which means in UI i want 10 rows with data from Api and balance should be empty.
You can fix in view layer, ts layer or even backend API layer (Not really recommended).
In view layer if you loop over your data, you can calculate if your data's size goes over arbitrary threshold and if not loop again to display as many empty rows as possible.
In ts layer, when you receive data from api you can modify variable you pass to your view by adding to an array as many empty items as you need.
What's important if you use null, then you have to check for it with for example elvis operator.
I would advise agains adding to an array an object with all properties set to null, because then these are not so easily distinguishable from valid data from API and you can for instance make some rows interactive, even though they should not be.
const dataFromApi = [{ "id": "1", "name": "name1" }, { "id": "2", "name": "name2" }]
const minRowsNumber = 10;
const diff = minRowsNumber - dataFromApi.length;
const viewTableData = diff > 0 ? dataFromApi.concat(new Array(diff).fill(null)) : dataFromApi;
console.log(viewTableData)
Example in AngularJs (No Angular in SO Snippets, but it is the same principle)
angular.module('example', [])
.controller('ExampleController', function ExampleController() {
const dataFromApi = [{ "id": "1", "name": "name1" }, { "id": "2", "name": "name2" }]
const minRowsNumber = 10;
const diff = minRowsNumber - dataFromApi.length;
this.viewTableData = diff > 0 ? dataFromApi.concat(new Array(diff).fill(null)) : dataFromApi;
});
table {
border-collapse: collapse;
}
td, th {
border: 1px solid black
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="example" ng-controller="ExampleController as example">
<table>
<thead>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in example.viewTableData track by $index">
<td>{{row ? row.id : ' '}}</td>
<td>{{row ? row.name : ' '}}</td>
</tr>
</tbody>
</table>
</div>
I am out of ideas how to populate dynamically my table. This table is result of endpoint response:
{props.types[2].length > 0 ? (
<div className="onepager-bottomtables-table ">
<h1 className="onepager-bottomtables-h1">UNIQUE Types</h1>
<table className="table table-striped table-bordered table-sm table-hover">
<thead className="thead-dark">
<tr>
<th>Type</th>
<th>Plan Name</th>
<th>More</th>
<th>Statistics</th>
</tr>
</thead>
<tbody className="table-hover">
{DataExtract.uniquePensionTypes(props.types[2]).map(
(element, index) => (
<tr key={index}>
<td className="align-middle" id="types-type">
{element}
</td>
<td className="align-middle">{element}</td>
<td className="align-middle">More</td>
<td>
//***THIS IS THE PROBLEM PART OF THE CODE********
<table className="onepager-small-table">
<thead>
<tr>
<th></th>
{DataExtract.uniqueYearsPension(props.types[2]).map(
(element, index) => (
<th key={index}>{element}</th>
)
)}
</tr>
</thead>
<tbody>
{console.log(
DataExtract.participantsPension(props.types[2])
)}
<tr>
<th>Participants</th>
</tr>
<tr>
<th>Total Asset</th>
</tr>
<tr>
<th>Net Asset</th>
</tr>
</tbody>
</table>
</td>
</tr>
)
)}
</tbody>
</table>
</div>
) : (
""
)}
I am pasting all the code to get the picture. The data I get (props.types[2]) looks like this for example:
"SumPensionTypes": [
{
"Type": "DefinedBenefitPension",
"Description": null,
"Year": 2016,
"Participants": 9.0,
"TotalAssets": 6668305.0,
"NetAssets": 6668305.0,
"PlanName": null
},
{
"Type": "DefinedContributionPension",
"Description": null,
"Year": 2016,
"Participants": 72.0,
"TotalAssets": 17230395.0,
"NetAssets": 17230395.0,
"PlanName": null
},
{
"Type": "DefinedBenefitPension",
"Description": null,
"Year": 2017,
"Participants": 7.0,
"TotalAssets": 2096999.0,
"NetAssets": 2096999.0,
"PlanName": null
},
{
"Type": "DefinedContributionPension",
"Description": null,
"Year": 2017,
"Participants": 56.0,
"TotalAssets": 16114639.0,
"NetAssets": 16114639.0,
"PlanName": null
},
{
"Type": "DefinedBenefitPension",
"Description": null,
"Year": 2018,
"Participants": 0.0,
"TotalAssets": 0.0,
"NetAssets": 0.0,
"PlanName": null
},
{
"Type": "DefinedContributionPension",
"Description": null,
"Year": 2018,
"Participants": 49.0,
"TotalAssets": 21954205.0,
"NetAssets": 21954205.0,
"PlanName": null
}
]
So currently all looks like this picture:
Now it comes the hard part (for me). I am trying to fill the small tables with the summed data for the objects with same type. As you can see from data there are 2 types - DefinedBenefitPension and DefinedContributionPension which repeats for 2016,2017,2018. I combined them with this method and populate them in first column:
//***************DIFFERENT PENSION TYPES********************* */
const uniquePensionTypes = data => {
const unique = [...new Set(data.map(plan => plan.Type))];
return unique;
};
The problem is that I want to populate small table as per plan type. If you see the picture and data you will understand how it is suppose to be populated as they have same types but statistics are year-for-year.
Here's some sample code to help.
getDataRow is a helper function that takes 3 arguments:
Your data (props.types[2])
The pension type ("DefinedBenefitPension" or "DefinedContributionPension")
The metric (like "Participants")
You can use the resulting object to map to table cells according to year.
https://repl.it/repls/UprightAdmiredBlogclient
// DataExtract.uniqueYearsPension(props.types[2])
const years = [2016, 2017, 2018]
function getDataRow(data, type, key) {
const rowData = {}
data.filter(pension => pension.Type == type).forEach(pension => {
rowData[pension.Year] = pension.Participants
})
return rowData
}
// { '2016': 9, '2017': 7, '2018': 0 }
const participants = getDataRow(data, 'DefinedBenefitPension', 'Participants')
// <td key=2016>9</td>
// <td key=2017>7</td>
// <td key=2018>0</td>
const row = years.map(year => {
return <td key={year}>
{participants[year]}
</td>
})
I have a slightly strange problem that i cant solve. I have to render a table using react - however my data structure is such that i need the name from the first level to be the table headings - I then need to map the data associated to that heading into the correct cells in the table.
Here is my data
[{
"cell_id": 1,
"step_data": [{
"width": 1920,
"name": "bruce",
}, {
"width": 2236,
"name": "ben",
}],
"cell_name": " boys names"
},
{
"cell_id": 2,
"step_data": [{
"width": 1920,
"name": "grace",
}, {
"width": 2236,
"name": "megan",
}],
"cell_name": "girls names"
}
To map the table headings i do this which dynamically renders the headings however I cant seem to put the data under the correct headings as they all go on the same row.
Any ideas how i do this?
My desired task is to have a table with two headings boys names and girls names - I need the step_data from boys names to be rows under the boys name heading and the step data from girls names to be under the girls names heading
<table className="table">
<tbody>
<tr>
{this.props.data.map((item, key) => <th key={key}>{item.cell_name}</th>
)}
</tr>
{this.props.data.map(data => data.map((z, key) => <td key={key}>{z.name}</td>))}
<tr>
</tr>
</tbody>
</table>
{[
...Array( // get the number of rows you need to create
Math.max(...this.props.data.map(({ step_data }) => step_data.length))
).keys()
].map(i => (
<tr key={i}>
{this.props.data.map(({ step_data, cell_name }) => (
<td key={cell_name}>{(step_data[i] || {}).name}</td>
))}
</tr>
))}
I am facing an issue to order the columns of a fetched API data horizontally. I am trying to print the data fetched from an API into a table. Anyhow the rows are well printed horizontally, but the function that I used for rows this.state.data.map() doesn't function in the same way for the columns. I think it's ES6 standard, but I am not sure. Here is my printed issue.
Here is my code sample:
class App extends React.Component
{
constructor()
{
super();
this.state = {
rows: [],
columns: []
}
}
componentDidMount()
{
fetch( "http://ickata.net/sag/api/staff/bonuses/" )
.then( function ( response )
{
return response.json();
} )
.then( data =>
{
this.setState( { rows: data.rows, columns: data.columns } );
} );
}
render()
{
return (
<div id="container" className="container">
<h1>Final Table with React JS</h1>
<table className="table">
<thead> {
this.state.columns.map(( column ) => (
<tr>
<th>{column[0]}</th>
<th>{column[1]}</th>
<th>{column[2]}</th>
<th>{column[3]}</th>
</tr>
) )}
</thead>
<tbody> {
this.state.rows.map(( row ) => (
<tr>
<td>{row[0]}</td>
<td>{row[1]}</td>
<td>{row[2]}</td>
<td>{row[3]}</td>
</tr>
) )
}
</tbody>
</table>
</div>
)
}
}
ReactDOM.render( <div id="container"><App /></div>, document.querySelector( 'body' ) );
I was able to print harcoded, if I give value to the 'th' elements, but I want to print it dynamically, in case the data within the API has been changed.
You are welcome to contribute directly to my Repo: Fetching API data into a table
Here is how looks like my example, when columns values has been hardcoded within 'th' elements.
Any suggestions will be appreciated!
var data = {
columns: [
"Full name",
"Job title",
"Age",
"Bonus",
],
rows: [
[
"John Smith",
"team lead front-end",
30,
444.08,
],
[
"Tom Jones",
"front-end developer",
25,
333.3,
],
[
"Deborah Barnes",
"front-end developer",
21,
233.66,
],
[
"Isaac Roberson",
"technical support",
44,
353,
],
[
"Josh Brown",
"team lead back-end",
35,
353,
],
[
"Chester Mckinney",
"back-end developer",
33,
223.27,
],
[
"Ora Burton",
"back-end developer",
32,
192.92,
],
[
"Jim Brown",
"technical support",
19,
98.99,
],
],
}
class App extends React.Component
{
constructor()
{
super();
this.state = {
rows: [],
columns: []
}
}
componentDidMount()
{
this.setState( { rows: data.rows, columns: data.columns } );
}
render()
{
return (
<div id="container" className="container">
<h1>Final Table with React JS</h1>
<table className="table">
<thead>
<tr>
{this.state.columns.map(( column, index ) => {
return (<th>{column}</th>)
}
)
}
</tr>
</thead>
<tbody> {
this.state.rows.map(( row ) => (
<tr>
<td>{row[0]}</td>
<td>{row[1]}</td>
<td>{row[2]}</td>
<td>{row[3]}</td>
</tr>
) )
}
</tbody>
</table>
</div>
)
}
}
ReactDOM.render( <div id="container"><App /></div>, document.querySelector( 'body' ) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Hope it may help
Not sure why you wanted to do columns[0], [1], [2] and [3]. Each column inside the map is a string. If you do columns[0] it will return you the first character from that which is what is happening in your case. Full Name, Job title, Age Bonus are rendered in your first image. First four characters in your columns.
The key attribute I added is just React's requirement to provide unique keys and has nothing to do with the problem.
this.state.columns.map((column, index) => (
<tr>
<th key={"columns-" + index.toString()}>
{column}
</th>
</tr>
))