Dynamically parse data for table rows - javascript

I am trying to parse the data dynamically for the table. So far I have tried the following to display the table.
renderTableData = () => {
return this.props.data.map((item, index) => {
const { name, value } = item;
return (
<tr key={index}>
<td>{name}</td>
<td>{value}</td>
</tr>
);
});
};
Here I am hardcoding the field values for displaying. I need this to be dynamic
Full code: https://codesandbox.io/s/react-basic-class-component-3kpp5?file=/src/Table.js:0-805
import * as React from "react";
class Table extends React.Component {
renderTableData = () => {
return this.props.data.map((item, index) => {
const { name, value } = item;
return (
<tr key={index}>
<td>{name}</td>
<td>{value}</td>
</tr>
);
});
};
renderTableHeader = () => {
let header = Object.keys(this.props.data[0]);
return header.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>;
});
};
render() {
return (
<div>
<table>
<tbody>
<tr>{this.renderTableHeader()}</tr>
{this.renderTableData()}
</tbody>
</table>
</div>
);
}
}
export default Table;

You can loop through object properties with Object.entries
renderTableData = () => {
return this.props.data.map((item, index) => {
return (
<tr key={index}>
{Object.entries(item).map(([key, value])=> <td key={key}>{value}</td>)}
</tr>
);
});
};
However, as you can see you lost control of the order of columns. Additionaly there might be columns you don't wish to display.
You can tackle that by appending Object.entries with custom implemented functions
<tr key={index}>
{Object.entries(item)
.filter(predicateFunction)
.sort(sortingFunction).map(([key, value])=> <td key={key}>{value}</td>)}
</tr>
Or switch to react-data-table

Related

How to group by two columns? ReactJS

The code that I posted below is the API request from which I make a table. This table has 4 columns: id, userid, title. I want to understand how I can sort by userid and title, as shown in the photo. It would be great if the steps were described in detail.
I'm trying to group the tab as shown in the photo, but I can't.
Can you suggest/show me how to do this?
Also wanted to know how to reset the group value of a column?
I will be grateful for any help.
My code:
import React from "react";
import "./GroupByUserID.css";
import { Link } from "react-router-dom";
export default class GroupByUserID extends React.Component {
// Constructor
constructor(props) {
super(props);
this.state = {
items: [],
};
}
componentDidMount = () => {
this.apiFetch();
};
//Fetch data from API
apiFetch = () => {
return fetch("https://jsonplaceholder.typicode.com/todos")
.then((res) => res.json())
.then((json) => {
this.setState((prevState) => {
return { ...prevState, items: json };
});
});
};
// Sort UserID
setSortedItemsUserID = () => {
const { items } = this.state;
const sortedUserID = items.sort((a, b) => {
if (a.userId < b.userId) {
return items.direction === "ascending" ? -1 : 1;
}
if (a.userId > b.userId) {
return items.direction === "ascending" ? 1 : -1;
}
return 0;
});
console.log(sortedUserID);
this.setState((prevState) => {
return { ...prevState, items: sortedUserID };
});
};
render() {
const { items } = this.state;
return (
<div>
<h1>Home Page</h1>
<table>
<thead>
<tr>
<th>
<Link target="self" to="/">
View Normal
</Link>
</th>
<th>Group By UserID</th>
</tr>
</thead>
<thead>
<tr>
<th>
User ID
<button
type="button"
onClick={() => this.setSortedItemsUserID()}
>
⬇️
</button>
</th>
<th>Title</th>
</tr>
</thead>
<tbody>
{items.map((item) => (
<tr key={item.userId + item.title}>
<td>{item.userId}</td>
<td>{item.title}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}

create table from JSON object in react

I am creating a table dynamically in React with the API response as it is as it receives.
data = {"name":"tom", "age":23, "group":null, "phone":xxx}
It is working fine with that, but the group key sometimes contains another object, so I want to create a sub-table whenever there is another object inside a key.
For example:
data = {"name":"tom", "age":23, "group":{"id":123, "joined_at":12-jan-2022}, "phone":xxx}
My current code is:
<table>
{Object.keys(data).map(index=> (
<tr>
<td>{index}</td>
<td>{data[index]?.toString()}</td>
</tr>
))}
</table>
The code you have shared would change as follows:
<table>
{Object.keys(data).map((item, index) => {
if (group) {
return (
<>
<tr>
<td>{index}</td>
<td>{item.toString()}</td>
</tr>
<tr>
<table>
<tr>
<td>{group.id}</td>
<td>{group.joined_at}</td>
</tr>
</table>
</tr>
</>
);
}
return (
<tr>
<td>{index}</td>
<td>{item.toString()}</td>
</tr>
);
})}
</table>;
You can fix the formatting as per your needs but this is essentially how you would create a sub-table based on the value of group
A slightly improved version would be :
const normalTableRow = (index, item) => {
return (
<tr>
<td>{index}</td>
<td>{item.toString()}</td>
</tr>
);
};
<table>
{Object.keys(data).map((item, index) => {
if (group) {
return (
<>
{normalTableRow(index, item)}
<tr>
<table>
<tr>
<td>{group.id}</td>
<td>{group.joined_at}</td>
</tr>
</table>
</tr>
</>
);
}
return normalTableRow(index, item);
})}
</table>;
EDIT: updating solution based on comment
const normalTableRow = (index, item) => {
return (
<tr>
<td>{index}</td>
<td>{item.toString()}</td>
</tr>
);
};
const isObject = (value) => {
return typeof value === 'object' && !Array.isArray(value) && value !== null;
};
<table>
{Object.keys(data).map((item, index) => {
let subTables = Object.keys(item).map((key) => {
if (isObject(item[key])) {
return (
<tr>
<table>
<tr>
{Object.keys(item[key]).map((subKey) => {
return <td>{item[key][subKey]}</td>;
})}
</tr>
</table>
</tr>
);
}
});
return (
<>
{normalTableRow(index, item)}
{[...subTables]}
</>
);
})}
</table>;

Search in custom table - React

I'm trying to make a table with api data searchable. I'm on my way, but unsure on how to proceed further.
Code looks like this:
const [searchTerm, setSearchTerm] = useState("");
const [searchResults, setSearchResults] = useState([]);
const handleChange = event => {
setSearchTerm(event.target.value);
};
useEffect(() => {
const results = apiData.filter(person =>
person.toLowerCase().includes(searchTerm)
);
setSearchResults(results);
}, [searchTerm]);
const renderPerson = (contact, index) => {
return (
<tr key={index}>
<td>{contact.ID}</td>
<td>{contact.Info.Name}</td>
<td>{contact.InfoID}</td>
<td>{contact.Info.DefaultEmail.EmailAddress}</td>
<td>{contact.Info.DefaultPhone.Number}</td>
<td>{contact.Info.InvoiceAddress.AddressLine1}</td>
</tr>
)
}
return (
<Fragment>
<input type="text" value={searchTerm} onChange={handleChange} placeholder="Søk.."></input>
<table id="myTable">
<thead>
<tr className="header">
<th>ID</th>
<th>Navn</th>
<th>Info Id</th>
<th>E-post</th>
<th>Telefon</th>
<th>Adresse</th>
</tr>
</thead>
<tbody>
{apiData.map(renderPerson)}
</tbody>
</table>
</Fragment>
)
https://dev.to/asimdahall/simple-search-form-in-react-using-hooks-42pg
I've followed this guide, but since I have the renderPerson function, I'm a bit unsure on how to handle this.
Question: Is there any way to get this working, or am I approaching it the wrong way? I'm aware that I need to put searchResult in the tbody somehow, but then the table won't be populated.
Any help is much appreciated
Edit: displaying code for getting apiData:
useEffect(() => {
getContacts()
}, [])
const getContacts = () => {
$.ajax({
url: `http://localhost:5000/getContacts`,
type: "POST",
data: ajaxObj,
success: (data) => {
let response = JSON.parse(data)
setApiData(response)
setLoading(false)
},
error: () => {
console.log("noe feilet?");
}
});
}
console.log(apiData)
Data looks like this:
data
Change apiData to searchResults on render
<tbody>
{searchResults.map(renderPerson)}
</tbody>
Change your filter way result (Updated)
const results = apiData.filter(person =>
person.Info.Name.toLowerCase().includes(searchTerm)
);
....
const renderPerson = (item, index) => {
return (
<tr key={index}>
<td>{item.ID}</td>
<td>{item.Info.Name}</td>
<td>{item.InfoID}</td>
<td>{item.Info.DefaultEmail.EmailAddress}</td>
<td>{item.Info.DefaultPhone.Number}</td>
<td>{item.Info.InvoiceAddress.AddressLine1}</td>
</tr>
)
}
Try this
export default function () {
const [searchTerm, setSearchTerm] = useState("");
const [searchResults, setSearchResults] = useState([]);
const apiData=[
{
ID:'1222',
Info:{
ID:'1222',
EmailAddress:'test#test.com',
Number:'2222222222',
AddressLine1:'test'
}
},
{
ID:'2333',
Info:{
ID:'2333',
EmailAddress:'test2#test.com',
Number:'1111111111',
AddressLine1:'test2'
}
}
]
const handleChange = event => {
setSearchTerm(event.target.value);
if(event.target.value){
const results = apiData.filter(person =>
person.ID.toLowerCase().includes(event.target.value)
);
setSearchResults(results);
}else{
setSearchResults([]);
}
};
const renderPerson = (contact, index) => {
return (
<tr key={index}>
<td>{contact.ID}</td>
<td>{contact.Info.Name}</td>
<td>{contact.Info.ID}</td>
<td>{contact.Info.EmailAddress}</td>
<td>{contact.Info.Number}</td>
<td>{contact.Info.AddressLine1}</td>
</tr>
)
}
return (
<Fragment>
<input type="text" value={searchTerm} onChange={handleChange} placeholder="Søk.."></input>
<table id="myTable">
<thead>
<tr className="header">
<th>ID</th>
<th>Navn</th>
<th>Info Id</th>
<th>E-post</th>
<th>Telefon</th>
<th>Adresse</th>
</tr>
</thead>
<tbody>
{searchResults.map(renderPerson)}
</tbody>
</table>
</Fragment>
)
}

Filter method on another method?

I want to run a filter method on the renderTable method that I've defined below. Is it possible to do:
renderTable().filter(x => x.name)?
Right now, I have a table with rows of each category and their results from a url that provided some json data.
I would like to make a feature that allows users to adjust the setting to return their desired results.
CODE
const renderTable = () => {
return players.map(player => {
return (
<tr>
<td>{player.name}</td>
<td>{player.age}</td>
<td>{player.gender}</td>
<td>{player.state}</td>
<td>{player.status}</td>
</tr>
)
})
}
return (
<div className = "App">
<h1>Players</h1>
<table id = "players">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Gender</th>
<th>State</th>
<th>Status</th>
</tr>
</thead>
<tbody>{renderTable()}</tbody>
</table>
</div>
);
You can apply the filter before the map. Here, give this a try:
const renderTable = () => {
return players
.filter(player => player.state === "NSW")
.map(player => {
return (
<tr>
<td>{player.name}</td>
<td>{player.age}</td>
<td>{player.gender}</td>
<td>{player.state}</td>
<td>{player.status}</td>
</tr>
);
});
};
Here's a Working Sample CodeSandbox for your ref.
Yes you can. Just filter before map players.filters(...).map. But this is better
const renderTable = () => {
const rows = []
players.forEach((player, index) => {
if(player.name) {
rows.push(
<tr key={index}>
<td>{player.name}</td>
<td>{player.age}</td>
<td>{player.gender}</td>
<td>{player.state}</td>
<td>{player.status}</td>
</tr>
)
}
})
return rows;
}

Why it is giving me 'Cannot read property 'deleteProduct' of undefined' error react Js

I am getting an error when deleting one row in react js. error is 'Cannot read property 'deleteProduct' of undefined'. also is there any simple way to delete data from the database using custom api. below is my complete code for deleting data from the database.
Here is my code for deleting row-
import React from 'react';
import ReactDOM from 'react-dom';
export default class FetchedData extends React.Component{
constructor(props){
super(props);
this.state={
UserData:[],
response: {}
};
this.headers=[
{key:1,label:'Name'},
{key:2,label:'Department'},
{key:3,label:'Marks'},
];
this.deleteProduct=this.deleteProduct.bind(this);
}
componentDidMount(){
this.lookupInterval = setInterval(() => {
fetch("https://www.veomit.com/test/zend/api/fetch.php")
.then(response => {
return response.json();
})
.then(result => {
this.setState({
UserData:result
})
.catch(error => {
console.log(
"An error occurred while trying to fetch data from Foursquare: " +error
);
});
});
}, 500)
}
deleteProduct(userId) {
const { UserData } = this.state;
const apiUrl = 'https://www.veomit.com/test/zend/api/delete.php';
const formData = new FormData();
formData.append('userId', userId);
const options = {
method: 'POST',
body: formData
}
fetch(apiUrl, options)
.then(res => res.json())
.then(
(result) => {
this.setState({
response: result,
UserData: UserData.filter(item => item.id !== userId)
});
},
(error) => {
this.setState({ error });
}
)
}
render(){
return(
<div>
<table class="table table-bordered">
<thead>
<tr>
{
this.headers.map(function(h) {
return (
<th key = {h.key}>{h.label}</th>
)
})
}
</tr>
</thead>
<tbody>
{
this.state.UserData.map(function(item, key) {
return (
<tr key = {key}>
<td>{item.name}</td>
<td>{item.department}</td>
<td>{item.marks}</td>
<td><span onClick={() => this.deleteProduct(item.id)}>Delete</span></td>
</tr>
)
})
}
</tbody>
</table>
</div>
);
}
}
Please help me remove this error.
thanks in advance.
Your mapping function is creating a new scope:
this.state.UserData.map(function(item, key) {
return (
<tr key = {key}>
<td>{item.name}</td>
<td>{item.department}</td>
<td>{item.marks}</td>
<td><span onClick={() => this.deleteProduct(item.id)}>Delete</span></td>
</tr>
)
})
Making it an arrow function should solve the issue:
this.state.UserData.map((item, key) => {
return (
<tr key = {key}>
<td>{item.name}</td>
<td>{item.department}</td>
<td>{item.marks}</td>
<td><span onClick={() => this.deleteProduct(item.id)}>Delete</span></td>
</tr>
)
})
This is probably due to losing context here:
{
this.state.UserData.map(function(item, key) {
return (
<tr key = {key}>
<td>{item.name}</td>
<td>{item.department}</td>
<td>{item.marks}</td>
<td><span onClick={() => this.deleteProduct(item.id)}>Delete</span></td>
</tr>
)
})
}
Change the function to an arrow function to autobind the callback:
{
this.state.UserData.map((item, key) => {
return (
<tr key = {key}>
<td>{item.name}</td>
<td>{item.department}</td>
<td>{item.marks}</td>
<td><span onClick={() => this.deleteProduct(item.id)}>Delete</span></td>
</tr>
)
})
}

Categories

Resources