I have a body object with different types of elements: (strings, number, objects...).
I need to show the body in a table.
In order to do it, I need to print in one table the elements that aren't objects, and in another table the elements that are objects.
So I am calling the function to create an array with object elements (arrObj) and another array with the non object elements (arrSimple).
The problem is that when I go through the arrSimple array to print the elements in a table, this array is empty.
Could anyone guide me on how can I resolve this async problem?
const DetailResult = props => {
...
const arrSimple = []
const arrObj = []
function organizeArray() {
for (const prop in body) {
if (typeof (body[prop]) != 'object') {
arrSimple[prop] = (body[prop])
} else if (typeof (body[prop]) == 'object') {
arrObj[prop] = (body[prop])
}
}
}
function renderGeneralData() {
organizeArray()
arrSimple.map((key, i) => {
<tr key={i}>
<td width="25%">{key}</td>
<td>{(arrSimple[key])}</td>
</tr>
})
}
return (
<div>
<table className='table table-striped'>
<tbody>
<tr>
<th>General Data</th>
</tr>
<tr>
{renderGeneralData()}
</tr>
</tbody>
</table>
</div>
)
}
export default DetailResult;
The body object comes from the app component.
class App extends Component {
constructor() {
super()
this.state = {
dataTable: {
transactionID: '',
maxRows: 10,
currentPage: 0,
list: {
headerList: [],
body: []
}
}
}
this.search = this.search.bind(this)
}
search() {
axios.get(URL)
.then(resp => this.setState({
dataTable: Object.assign(this.state.dataTable, {
list: [
{headerList: ['App name', 'Date', 'Bio data', 'Is verified', 'Actions']},
{body: resp.data},
],
}),
}))
.catch(function (error) {
console.log(error);
})
}
I have a component that contains a search field to make a request
const SearchComponent = props => {
const renderDetailResult =
<DetailResult list={props.dtObject.list}
search={props.search}
/>
return (
<div role='form' className='searchID'>
<ContentHeader title={props.componentHeaderTitle} />
<Grid cols='12 9 10'>
<input id="cpf" className='w-25 form-control'
placeholder='Type the ID'
/>
</Grid>
<Grid cols='12 3 2'>
<IconButton style='primary' icon='search'
onClick={props.search}>
</IconButton>
</Grid>
<Grid cols='12'>
{renderDetailResult}
</Grid>
</div>
)
}
export default SearchComponent
The reason why nothing appears is that you are calling a function that returns nothing, so there isn't anything to render.
You need to return the .map and return the elements you want.
function renderGeneralData() {
organizeArray()
// added return
return arrSimple.map((key, i) => (
<tr key={i}>
<td width="25%">{key}</td>
<td>{(arrSimple[key])}</td>
</tr>
))
}
Observation
You are rendering <tr> inside <tr>. I recommend removing the
return arrSimple.map((key, i) => (
//returning tr
<tr key={i}>
<td width="25%">{key}</td>
<td>{(arrSimple[key])}</td>
</tr>
))
<tr>
// returned tr inside another tr
{renderGeneralData()}
</tr>
I'm not sure how you want to display your data, but I recommend removing one of the tr tag.
Quick tip
If you want to remove the tr that is inside .map you should use React.Fragment
return arrSimple.map((key, i) => (
<React.Fragment key={i}>
<td width="25%">{key}</td>
<td>{(arrSimple[key])}</td>
</React.Fragment>
))
Edit:
I also noticed something weird in your code in this part
arrSimple.map((key, i) => (
<tr key={i}>
<td width="25%">{key}</td>
<td>{(arrSimple[key])}</td>
</tr>
))
In this part of the code, key will be an element of arrSimple. If you do arrSimple[key] it will probably return undefined. Here is an example
arr = ['hey', 'this', 'is', 'bad', '!']
console.log(arr.map((key, i) => arr[key]))
Related
I need to display 3 types of data with different fields in the same table. To do this, I want to have 3 headers with a different color each.
I use bootstrap to make my design and my code is in Javascript with React.
I wrote the following code to do this (I tried to simplify it but it is normally reproducible)
import * as React from "react";
import { useEffect, useState } from "react";
import { nanoid } from "nanoid";
//props object
type IPropsTable={
currentDatas: (DataType1 | DataType2 | DataType3 | undefined;
}
const TableRequest: React.FC<IPropsTable> = ({ currentDatas }) => {
const [existData1, setExistData1] = useState(false);
const [existData2, setExistData2] = useState(false);
const [existData3, setExistData3] = useState(false);
useEffect(()=>{
if (currentDatas) {
currentDatas.map((currentData) => {
if (currentData.type === "data1") {
setExistData1(true);
} else if (currentData.type === "data2") {
setExistData2(true);
} else if (currentData.type === "data3") {
setExistData3(true);
}
})
}
},[currentDatas])
function renderTableHeaderData1() {
let header = ['someField1', 'someField2']
return header.map((key, index) => {
return <th key={index} scope="col">{key.toUpperCase()}</th>
})
}
function renderTableHeaderData2() {
let header = ['someOtherField1', 'someOtherField2']
return header.map((key, index) => {
return <th key={index} scope="col">{key.toUpperCase()}</th>
})
}
function renderTableHeaderData3() {
let header = ['someOtherOtherField1', 'someOtherOtherField2']
return header.map((key, index) => {
return <th key={index} scope="col">{key.toUpperCase()}</th>
})
}
function renderTableData() {
if(currentDatas){
return currentDatas.map((session) => {
if (session.type === "data1") {
return (
<tr key={nanoid()} className="warning">
<td>{session.someField1}</td>
<td>{session.someField2}</td>
</tr>
)
} else if (session.type === "data2") {
return (
<tr key={nanoid()} className="info">
<td>{session.someOtherField1}</td>
<td>{session.someOtherField2}</td>
</tr>
)
} else if (session.type === "data3") {
return (
<tr key={nanoid()} className="success">
<td>{session.someOtherOtherField1}</td>
<td>{session.someOtherOtherField2}</td>
</tr>
)
}
})
} else{return undefined}
}
return (
<>
<div>
<table className="table table-sm">
<caption>Result Search</caption>
<thead>
{existData1?
<tr className="thead-warning">{renderTableHeaderData1()}</tr>
: <></>
}
{existData2?
<tr className="thead-info">{renderTableHeaderData2()}</tr>
: <></>
}
{existData3?
<tr className="thead-success">{renderTableHeaderData3()}</tr>
: <></>
}
</thead>
<tbody>
{renderTableData()}
</tbody>
</table>
</div>
</>
)
}
export default TableRequest;
As you can see in the code above, I assign a css class to each of my <tr> (warning for data1, info for data2 and success for data3). But when my component is rendered, no color appears and the table is completely white, either for each of the three headers or for the data contained in each row of the table.
I tried using the thead-warning, thead-info and thead-success classes for my table header tr css classes, they seemed to be more suitable. But same result, no color is displayed.
Does anyone see what I'm doing wrong and could guide me in the right direction, I really don't understand where my problem is.
Use <tr class="success"> or <tr class="warning"> (depends what color you want).
Example:
<tr class="success">
<td>Success</td>
<td>Doe</td>
<td>john#example.com</td>
</tr>
My problem was with the name of the className I was using to color my table. By using bg-success instead of table-success everything works normally. But I don't understand why the table-success class doesn't work as in this example: example with table-success
At the moment, all the available flights that was received from API are successfully loaded on the page. However, I would like to enable the end user to search specific flight, let's say, by flight number and departure date. How can I integrate this searching functionality in the existing codes?
FlightPage.js
render() {
return (
<>
<h2>Flights</h2>
{this.props.loading ? (
<div>Loading...</div>
) : (
<FlightList flights={this.props.flights} />
)}
</>
);
}
}
As you can see the bellow code, I have used table to present the results.I would like to show only one result or blank table when searching is applied. Can you help me to achieve this?
FlightList.js
const FlightList = ({ flights }) => (
<table className="table">
<thead>
<tr>
<th />
<th>Date</th>
<th>Provider</th>
<th>Dest</th>
</tr>
</thead>
<tbody>
{flights.map((f, i) => {
return (
<tr key={i}>
<td>
<input type="checkbox" name="flightListCheckbox" />
</td>
<td>{f.date}</td>
<td>{f.pnr}</td>
<td>{f.flightNumber}</td>
</tr>
);
})}
</tbody>
</table>
);
You could use filter to create a searching functionality like
I would at first add an input where I can insert my filter values
FlightPage.js
handleInput: (event) => {
const { name, value } = event.target
this.setState({ [name]: value })
}
render () {
const { filter } = this.state
return (
<>
<input onChange=(this.handleInput) value={filter} name='filter' />
<FlightList flights={this.props.flights} filterValues={filter} />
</>
)
}
Then I would use my state to filter my Object like
FlightList.js
const FlightList = ({ flights, filterValue }) => {
const filterdFlights = flights.filter(flight => Object.values(flight).includes(filterValue))
return (
<table className="table">
<thead>
<tr>
<th />
<th>Date</th>
<th>Provider</th>
<th>Dest</th>
</tr>
</thead>
<tbody>
{filterdFlights.map((f, i) => {
return (
<tr key={i}>
<td>
<input type="checkbox" name="flightListCheckbox" />
</td>
<td>{f.date}</td>
<td>{f.pnr}</td>
<td>{f.flightNumber}</td>
</tr>
);
})}
</tbody>
</table>
)};
You need an input for search and filter flights by value of input. Try this
class FlightPage extends React.Component {
state = {
keyword: '',
}
...
getFlights = () => {
const { keyword } = this.state
const { flights } = this.props
return flights.filter(flight => flight.name.includes(keyword)) // name or something else
}
onInputChange = e => {
this.setState({ keyword: e.target.value })
}
render () {
return (
<>
<input onChange=(this.onInputChange) value={this.state.keyword} />
<FlightList flights={this.getFlights()} />
</>
)
}
}
You can filter your flights array using flights.filter or sort it using flights.sort.
You could try to use jquery datatable. It adds a lot of funcionality to tables easy to implement.
DataTable doc
I want to create entire table from a JSON array include dynamic ths, so a decoupled the head part to:
import React from 'react';
import TableDataTR from './TableTRView';
const TableView = ({ thds, tableData }) => {
if (!thds) {
return <table></table>;
}
return (
<div>
<table>
<thead>
<tr>
{thds.map((data, i) => <th key={i}>{data.th}</th>)}
</tr>
</thead>
<TableDataTR tableData={tableData}/>
</table>
</div>
)
}
export default TableView;
And the table prints the head part, but I am having trouble printing my tbody:
import React, { Component } from 'react';
class TableDataTR extends Component {
render() {
let rows = this.props.tableData.map((currElement, index) =>{
return(
<tr key={index}>
<td>currElement[index]</td>
</tr>
)
});
return (
<tbody>{rows}</tbody>
);
}
}
export default TableDataTR;
Example data, table head
thds : [ { th: 'th1' },
{ th: 'th2' }]
tableData:[ { tdData1: 'somedata', tdData2: 'somedata2' },
{ tdData1: 'somedata', tdData2: 'somedata2' },]
The table head works fine, but the data inside tbody is not displaying at all.
The final goal is to get any 2 arrays and display the table accordingly.
You need to access the table data using key name as
let rows = this.props.tableData.map((currElement, index) => {
return (
<tr>
{ Object.keys(currElement).map((item) =>
(
<td>{currElement[item]}</td>
)
)}
</tr>
)
});
There is a typo again in your <td>..</td> part, missing {} for your expression. For the dynamic solution after first map you need to map your Objects as well.
<tr key={index}>
<td>
{
Object.keys(currElement).map(key => currElement[key])
}
</td>
</tr>
I can't achieve to have two nested map:
render() {
return (
<table className="table">
<tbody>
{Object.keys(this.state.templates).map(function(template_name) {
return (
<tr key={template_name}><td><b>Template: {template_name}</b></td></tr>
{this.state.templates[template_name].items.map(function(item) {
return (
<tr key={item.id}><td>{item.id}</td></tr>
)
})}
)
})}
</tbody>
</table>
)
}
This gives a SyntaxError: unknown: Unexpected token.
How do you nest map calls in JSX?
You need to wrap it inside an element.
Something like this (I've added an extra tr due to the rules of tables elements):
render() {
return (
<table className="table">
<tbody>
{Object.keys(templates).map(function (template_name) {
return (
<tr key={template_name}>
<tr>
<td>
<b>Template: {template_name}</b>
</td>
</tr>
{templates[template_name].items.map(function (item) {
return (
<tr key={item.id}>
<td>{item}</td>
</tr>
);
})}
</tr>
);
})}
</tbody>
</table>
);
}
}
Running Example (without a table):
const templates = {
template1: {
items: [1, 2]
},
template2: {
items: [2, 3, 4]
},
};
const App = () => (
<div>
{
Object.keys(templates).map(template_name => {
return (
<div>
<div>{template_name}</div>
{
templates[template_name].items.map(item => {
return(<div>{item}</div>)
})
}
</div>
)
})
}
</div>
);
ReactDOM.render(<App />, document.getElementById('root'));
<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>
<div id="root"></div>
I struggled for a while to get my nested map function to work only to discover that what you return is critical. Make sure you are returning the second map itself and not just the final expected output:
let { categories } = data;
categories = categories.map(category =>
category.subcategories.map((subcategory, i) => <h2 key={i}>{subcategory.name}</h2>)
);
return (
<div className="category-container">
<div>{categories}</div>
</div>
);
I'm not sure if it's correct technically, but as a mnemonic you can remember that: "Every returned JSX element must be only one JSX element".
So most of the times just wrapping what you have in a <></> pair (or any other arbitrary tag pair) will fix the issue. E.g., if you're returning two <div>s from the render method of a component, that will be incorrect, however, if you wrap these two in a <></> pair, most probably it will be fixed.
But notice that sometimes it can get a bit more vague, e.g., when nesting two ES6 maps in each other, for example:
<tbody>
{
this.categorizedData.map(
(catgGroup) => (
<tr>
<td>{catgGroup}</td>
</tr>
this.categorizedData[catgGroup].map(
(item) => (
<tr>
<td>{item.name}</td>
<td>{item.price}</td>
</tr>
)
)
)
)
}
</tbody>
Can be fixed like this:
<tbody>
{
this.categorizedData.map(
(catgGroup) => (
<> // <--- Notice this, it will wrap all JSX elements below in one single JSX element.
<tr>
<td>{catgGroup}</td>
</tr>
{this.categorizedData[catgGroup].map( // <--- Also notice here, we have wrapped it in curly braces, because it is an "expression" inside JSX.
(item) => (
<tr>
<td>{item.name}</td>
<td>{item.price}</td>
</tr>
)
)}
</>
)
)
}
</tbody>
P.S.: (From documentation): You can also return an array of elements from a React component:
render() {
// No need to wrap list items in an extra element!
return [
// Don't forget the keys :slight_smile:
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}
I think the problem is that the return type should be an array but not an object in React16. You could try like this below:
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
templates: {
foo: {
items: [
{id: 0},{id:1}
]
},
bar: {
items: [
{id: 2},{id:3}
]
}
}
}
}
renderTemplate = (template, name) => {
let data = []
data = template.items
data.unshift({ name: name })
return data.map((item, index) => <tr key={index}><td>{item.name ? item.name : item.id}</td></tr>)
}
render() {
return (
<table>
<tbody>
{Object.keys(this.state.templates).map(name => {
return this.renderTemplate(this.state.templates[name], name)
})}
</tbody>
</table>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
td {
color: white;
padding: 0 20px;
background: grey;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.1.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.1.1/umd/react-dom.production.min.js"></script>
I am building a simple app in ReactJS that works with a JSON array by calling a certain API. I am then populating the results of the array in a table. What I now want is to click on any row in the table and get those values to pass into some other component. I am wondering how to get the row information using onClick.
Here is my code.
class ParentComponent extends Component {
constructor(props){
super(props);
this.state = {data: []};
}
componentDidMount() {
fetch('http://hostname:xxxx/yyyy/zzzz')
.then(function(response) {
return response.json();
})
.then(items=>this.setState({data: items}));
}
fetchAccountDetails () {
}
render(){
var newdata = this.state.data;
return (
<table className="m-table">
<thead>
<tr>
<th>AccountName</th>
<th>ContractValue</th>
</tr>
</thead>
<tbody>
{
newdata.map(function(account, index){
return (
<tr key={index} data-item={account} onClick={this.fetchAccountDetails()}>
<td data-title="Account">{account.accountname}</td>
<td data-title="Value">{account.negotiatedcontractvalue}</td>
</tr>
)
}
)
}
</tbody>
</table>
);
}
}
export default ParentComponent;
Pass the index of the state element and retrieve from the state array. Also it is not required to copy state to another variable before mapping, you can do it with state itself
render(){
return (
<table className="m-table">
<thead>
<tr>
<th>AccountName</th>
<th>ContractValue</th>
</tr>
</thead>
<tbody>
{
this.state.data.map((account, index) => {
return (
<tr key={index} data-item={account} onClick={() => this.fetchAccountDetails(index)}>
<td data-title="Account">{account.accountname}</td>
<td data-title="Value">{account.negotiatedcontractvalue}</td>
</tr>
)
}
)
}
</tbody>
</table>
);
}
}
fetchAccountDetails(index) {
var values = this.state.data[index];
console.log(values);
}
fetch the value using
fetchAccountDetails (event) {
Account:event.target.value
this.getData(Account);
}
getData: function(var account)
this.setState({
state: account
});
Now you have your data set as state and you can use it anywhere you want yo