Rendering and Grouping using React Tables - javascript

I am creating a dynamic table using react based on objects.
The problem is that I am trying to render a new row for every three TD, but instead it is rendering all TDs in the same table row (tr).
How can I separate the TDs into az new TR after a group of 3 TDs have been filled?
Here is my code:
Component rendering table:
`export class Roster extends React.Component{
render(){
return(
<div id="tab">
<table >
<tbody>
<tr>
<td colSpan={3}>
<h4>Goalkeepers</h4>
</td>
</tr>
</tbody>
<Goalies/>
</div>
)
}
}`
Component looping through object:
`class Goalies extends React.Component{
getTable() {
let td_array = [];
let goal_object = {};
Object.keys(goalies).forEach(function(key) {
goal_object = (goalies[key]);
});
for(const checker in goal_object){
td_array.push(<td key={checker}>{goal_object[checker].p_name} <br/> {goal_object[checker].city} <br/> {goal_object[checker].number}</td>);
}
return(
<tr>
{td_array}
</tr>
)
}
render() {
return (<tbody>{this.getTable()}</tbody>)
}
}`
Objects:
export const def = {
1:{
p_name:"p1",
number: 2,
city: "city1"
},
2:{
p_name:"p2",
number: 5,
city: "city2"
},
3:{
p_name:"p3",
number: 3,
city: "city3"
},
4:{
p_name:"p4",
number: 7,
city: "city4"
},
5:{
p_name:"p5",
number: 15,
city: "city5"
},
6:{
p_name:"p6",
number: 21,
city: "city6"
}
}
I want to get:
td1
td2
td3
td4
td5
td6
instead, I am getting:
td1
td2
td3
td4
td5
td6
I tried using conditional statements, pushing into a new array...

In order to achieve the layout you're after, the correct HTML markup would look like this:
<table>
<thead>
<tr>
<th colspan="3">
<h4>Goalkeepers</h4>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>td1</td>
<td>td2</td>
<td>td3</td>
</tr>
<tr>
<td>td4</td>
<td>td5</td>
<td>td6</td>
</tr>
</tbody>
</table>
First, you'll want to group your items into arrays of n (3 in your case). I'd use a generator function:
function* toChunks(arr, n) {
for (let i = 0; i < arr.length; i += n) {
yield arr.slice(i, i + n)
}
}
const rows = [...toChunks(items, 3)]
As an alternative, you could also use:
const chunk = (arr, n) =>
[...Array.from({ length: Math.ceil(arr.length / n) }).keys()].map((key) =>
arr.slice(key * n, (key + 1) * n)
)
const rows = chunk(items, 3)
Given the above rows, your component would look like this:
const Goalies = ({ rows }) => (
<tbody>
{rows.map((row, rk) => (
<tr key={rk}>
{row.map((cell, ck) => (
<td key={ck}>{cell.number}</td>
))}
</tr>
))}
</tbody>
)
Demo here
However, by looking at your data I think you shouldn't be using a <table> here at all. The layout you're after could easier be achieved with:
const Cells = ({ items }) => (
<div className="myWrapper">
{items.map((item, key) => (
<div key={key}>{item.number}</div>
))}
</div>
)
along with any of the following CSS models:
.myWrapper {
display: flex;
flex-wrap: wrap;
}
.myWrapper > * {
width: calc(100%/3)
}
/* or alternatively: */
.myWrapper {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr))
}

Related

React Table Increment Button

I'm very new to React, and I'm trying to make a table using react that displays information about a car. Currently, I'm trying to implement two buttons on each row that would increment/decrement a certain field of the table.
This is the code for the table:
render() {
return (
<table>
<tr>
<th>Manufacturer</th>
<th>Model</th>
<th>Year</th>
<th>Stock</th>
<th>Price</th>
<th>Options</th>
<th></th>
</tr>
{
this.state.cars.map(car =>
<tr key={car.model}>
<td>{car.manufacturer}</td>
<td>{car.model}</td>
<td>{car.year}</td>
<td>{car.stock}</td>
<td>${car.price}</td>
<td><button type="button" onClick={this.increaseStock.bind(this)}>Increment</button></td>
<td><button type="button" onClick={this.decreaseStock.bind(this)}>Decrement</button></td>
</tr>
))
}
</table>
);
};
This so far is just fine for displaying the data and buttons, however, I'm not actually sure how to implement the buttons' functions
All the implementations for increaseStock and decreaseStock so far have done nothing on click, although I think they're being called. I'm trying to get it to either add to the stock value or subtract from it(not going below 0).
I've tried things using states, but I'm not really sure how those work.
Here's an example of how your component could be implemented. I used the functional component and a state hook because those are what I'm familiar with.
import React, { useState } from "react";
export default function CarTable() {
const [cars, setCars] = useState([
{ id: 1, name: "Car 1", stock: 5 },
{ id: 2, name: "Car 2", stock: 10 }
]);
const increase = (index) => {
const oldCar = cars[index];
// copying the car with the updated stock
const newCar = { ...oldCar, stock: oldCar.stock + 1 };
//copying the car array
const newCars = [...cars];
//replacing the old car with a new one in a new array
newCars[index] = newCar;
//updating state
setCars(newCars);
};
const decrease = (index) => {
// same as above except decreasing the stock now
const oldCar = cars[index];
const newCar = { ...oldCar, stock: oldCar.stock - 1 };
const newCars = [...cars];
newCars[index] = newCar;
setCars(newCars);
};
return (
<table>
<thead>
<tr>
<th>name</th>
<th>stock</th>
<th></th>
</tr>
</thead>
<tbody>
{cars.map((car, i) => (
<tr key={car.id}>
<td>{car.name}</td>
<td>{car.stock}</td>
<td>
<button onClick={() => increase(i)}>increase</button>
<button onClick={() => decrease(i)}>decrease</button>
</td>
</tr>
))}
</tbody>
</table>
);
}
In short what you need to do is create a new car object with the updated stock property. Then create a new cars array with the new car object just mentioned and then set the state to this new cars array.
Here's also a CodeSandbox link where you can try it out:
https://codesandbox.io/s/charming-dijkstra-8brqz1?file=/src/CarTable.js
Hope that helps.

How to input array into 1 column table

How to input array into 1 column table ? Audience number 2 have 2 Films B & C. I want to become 1 column. ex [Film B, Film C] in Column Film.
My Table =>
The Table Image
const AudienceListComponent = () => {
const [audience, setAudience] = useState([])
console.log("audience", audience)
const getAllAudience = () => {
AudienceService.getAllAudiences().then((response) => {
console.log('response', response)
setAudience(response.data)
})
}
useEffect(() => {
getAllAudience()
}, [])
return (
<div>
<table className="table table-bordered table-bordered">
<thead className="table-dark">
<tr>
<th>No</th>
<th>Audience Name</th>
<th>Film</th>
</tr>
</thead>
<tbody>
{
audience.map((aud, i) => (
<tr key={aud.id}>
<td>{i + 1}</td>
<td>{aud.name}</td>
{aud.film.map(films =>
<td>{films.title}</td>
)}
</tr>
))
}
</tbody>
</table>
</div>
);
};
export default AudienceListComponent;
Solved by #Hamza Khan in a comment:
You need to run the map loop inside your td element like this: <td> {aud.film.map(films => films.title )} </td>

generating table rows for objects with children

Im trying to display a bunch of objects in table format. If the object has a child, i'd like it displayed in the next row before continuing down the array to the next object.
In my example below I successfully render a table showing all top level or parent objects:
const renderCategories = (categories) => {
let myCategories = [];
for(let category of categories){
myCategories.push(
<tr key={category.name}>
<td><input type="checkbox"></input></td>
<td>{category.name}</td>
</tr>
);
}
return myCategories;
}
return(
<div>
<table>
<thead>
<tr>
<th><input type="checkbox"></input></th>
<th>Name</th>
</tr>
</thead>
<tbody>
{renderCategories(category.categories)}
</tbody>
</table>
</div>
)
};
In my original version i wasn't using a table but instead using lists and so achieved what i was looking for by doing something like:
const renderCategories = (categories) => {
let myCategories = [];
for(let category of categories){
myCategories.push(
<li key={category.name}>
{category.name}
{category.children.length > 0 ?
{<ul>renderCategories(category.children)}</ul>) : null}
</li>
);
}
return myCategories;
}
This solution obviously wont work for my new use case as I cant render table rows inside table rows. This is driving me crazy as no alternative seems to work. I could achieve my desired design using divs instead of a table. However I want to use a table, or at least know how to do it by using a table.
Anyone able to point me in the right direction? thanks
Use Array.flatMap() and array spread to return a flattened array of <tr> elements.
Note: I've also added the indentation using level and empty <td> elements. You remove the Array.from() line, and the level if not needed.
const renderCategories = (categories, level = 0) =>
categories.flatMap(({ name, children }) => {
const cat = (
<tr key={name}>
<td><input type="checkbox"></input></td>
{Array.from({ length: level }, (_, i) => <td key={i}></td>)}
<td>{name}</td>
</tr>
);
return children.length ?
[cat, ...renderCategories(children, level + 1)]
:
cat;
});
const Demo = ({ categories }) => (
<div>
<table>
<thead>
<tr>
<th><input type="checkbox"></input></th>
<th>Name</th>
</tr>
</thead>
<tbody>
{renderCategories(categories)}
</tbody>
</table>
</div>
);
const categories = [{ name: 'A', children: [{ name: 'A1', children: [] }, { name: 'A2', children: [] }] }, { name: 'B', children: [{ name: 'B1', children: [{ name: 'B1-1', children: [] }, { name: 'B1-2', children: [] }] }, { name: 'B2', children: [] }] }]
ReactDOM.render(
<Demo categories={categories} />,
root
)
th, td {
border: 1px solid black;
}
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>

how can i add new row my react bootstrap table

I created the table I mentioned below using React js. When I click on the button below the table, I want to add a new row to the table. I have listed the react code I wrote below. how can I do that?
My React Code
const PPP13 = (props) => {
return (
<Jumbotron>
<p className="btn-group">13- List all owners of 20% or more of the equity of the Applicant</p>
<Table striped bordered hover>
<thead>
<tr>
<th>Owner Name</th>
<th>Title</th>
<th>Ownership %</th>
<th>TIN (EIN, SSN)</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<FormControl aria-label="DDD"/>
</td>
<td>
<FormControl aria-label="DDD"/>
</td>
<td>
<FormControl aria-label="DDD"/>
</td>
<td>
<FormControl aria-label="DDD"/>
</td>
<td>
<FormControl aria-label="DDD"/>
</td>
</tr>
</tbody>
</Table>
<Button className="btn-group" name="add" value="No">
Add more owners
</Button>
</Jumbotron>
)
}
Here is what you can do. Lets say you have a Main component which will get all details.
class Products extends React.Component {
constructor(props) {
super(props);
// this.state.products = [];
this.state = {};
this.state.filterText = "";
this.state.products = [
{
id: 1,
category: 'Sporting Goods',
price: '49.99',
qty: 12,
name: 'football'
}, {
id: 2,
category: 'Sporting Goods',
price: '9.99',
qty: 15,
name: 'baseball'
}, {
id: 3,
category: 'Sporting Goods',
price: '29.99',
qty: 14,
name: 'basketball'
}, {
id: 4,
category: 'Electronics',
price: '99.99',
qty: 34,
name: 'iPod Touch'
}, {
id: 5,
category: 'Electronics',
price: '399.99',
qty: 12,
name: 'iPhone 5'
}, {
id: 6,
category: 'Electronics',
price: '199.99',
qty: 23,
name: 'nexus 7'
}
];
}
handleAddEvent(evt) {
var id = (+ new Date() + Math.floor(Math.random() * 999999)).toString(36);
var product = {
id: id,
name: "empty row",
price: "mpty row",
category: "mpty row",
qty: 0
}
this.state.products.push(product);
this.setState(this.state.products);
}
handleProductTable(evt) {
var item = {
id: evt.target.id,
name: evt.target.name,
value: evt.target.value
};
var products = this.state.products.slice();
var newProducts = products.map(function(product) {
for (var key in product) {
if (key == item.name && product.id == item.id) {
product[key] = item.value;
}
}
return product;
});
this.setState({products:newProducts});
};
render() {
return (
<div>
<ProductTable onProductTableUpdate={this.handleProductTable.bind(this)} onRowAdd={this.handleAddEvent.bind(this)} products={this.state.products} />
</div>
);
}
}
This contains the code for adding row.Then for the table do something like this.
class ProductTable extends React.Component {
render() {
var onProductTableUpdate = this.props.onProductTableUpdate;
var product = this.props.products.map(function(product) {
return (<ProductRow onProductTableUpdate={onProductTableUpdate} product={product} key={product.id}/>)
});
return (
<div>
<button type="button" onClick={this.props.onRowAdd} className="btn btn-success pull-right">Add</button>
<table className="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>price</th>
<th>quantity</th>
<th>category</th>
</tr>
</thead>
<tbody>
{product}
</tbody>
</table>
</div>
);
}
}
Now for the row Comoponent:
class ProductRow extends React.Component {
render() {
return (
<tr className="eachRow">
<td>
{this.props.product.id}
</td>
<td>
{this.props.product.price}
</td>
<td>
{this.props.product.qty}
</td>
<td>
{this.props.product.category}
</td>
</tr>
);
}
}
Working Example:
https://jsfiddle.net/mrAhmedkhan/nvgozjhy/
Ok here's my plan:
First we create a state to hold all the data for the table. I've used an object instead of an array as it's much easier to do the change handling. With arrays you always end up doing all this awkward splicing. You can always parse the object out into an array when you're ready to use it elsewhere.
Then we render out each row of the table by mapping over the entries in our table state. Note we also write the change handler inside the map, meaning we can easily use the rowId (tableData key) to set our new state when a change comes in.
Finally we plop in a button to add more rows. This has a click handler associated with it (handleAddRowClick) which counts the number of rows we have and uses this to generate a new rowId. We use the new rowId to expand the tableData state to include a new defaultRow. I defined defaultRow outside of the function, this prevents it from being redeclared on every render.
import React, { useState } from 'react'
import { Table, Input, Button } from 'reactstrap'
const defautRow = { colA: '', colB: '' }
const IncreasableTable = props => {
const [tableData, setTableData] = useState({
row1: { colA: '', colB: '' }
})
const handleAddRowClick = () => {
const extantRowsCount = Object.keys(tableData).length
setTableData(s => ({
...s,
[`row${extantRowsCount}`]: defautRow
}))
}
return (
<>
<Table>
{
Object.entries(tableData).map(([rowId, data]) => {
const handleChange = ({ target: { name, value } }) => {
setTableData(s => ({
...s,
[rowId]: {
...s[rowId],
[name]: value
}
}))
}
return (
<tr key={rowId}>
<td>
<Input name="colA" value={data.colA} onChange={handleChange}/>
<Input name="colB" value={data.colB} onChange={handleChange}/>
</td>
</tr>
)
})
}
</Table>
<Button onClick={handleAddRowClick}>Click me to add more rows</Button>
</>
)
}
export default IncreasableTable

React.js - Creating simple table

Sorry for what it appears to be a newbie question (been up working late and just got started with React) but I am trying to figure out how to just render a simple table with nxn dimension.
For instance, in my component, the render output would be something like this:
<table id="simple-board">
<tbody>
<tr id="row0">
<td id="cell0-0"></td>
<td id="cell0-1"></td>
<td id="cell0-2"></td>
</tr>
<tr id="row1">
<td id="cell1-0"></td>
<td id="cell1-1"></td>
<td id="cell1-2"></td>
</tr>
<tr id="row2">
<td id="cell2-0"></td>
<td id="cell2-1"></td>
<td id="cell2-2"></td>
</tr>
</tbody>
</table>
where each row has it's own id , as well as each cell.
The initial state
constructor(props){
super(props);
this.state = {size: 3}
}
is what set the size of the table.
What threw me of was how to implement a for loop inside the JSX.
After a good night sleep, I was able to figure it out:
import React, { Component } from 'react'
export default class Example extends Component {
constructor(props){
super(props);
this.state = {size: 3}
}
render(){
let rows = [];
for (var i = 0; i < this.state.size; i++){
let rowID = `row${i}`
let cell = []
for (var idx = 0; idx < this.state.size; idx++){
let cellID = `cell${i}-${idx}`
cell.push(<td key={cellID} id={cellID}></td>)
}
rows.push(<tr key={i} id={rowID}>{cell}</tr>)
}
return(
<div className="container">
<div className="row">
<div className="col s12 board">
<table id="simple-board">
<tbody>
{rows}
</tbody>
</table>
</div>
</div>
</div>
)
}
}
Thanks all for responding!
One option (move stuff out of render():
import React from 'react';
import SimpleListMenu from '../menu/SimpleMenuListMenu'; // < Material UI element
let rows = [
'Setting One',
'Setting Two',
'Setting Three',
'Setting Four',
];
export default class MyTable extends React.Component {
createTable = () => {
let table = []
for (let i = 0; i < rows.length; i++) {
let children = []
children.push(<td>{rows[i]}</td>, <td>{<SimpleListMenu />}</td>)
table.push(<tr>{children}</tr>)
}
return table
}
render() {
return(
<table>
{this.createTable()}
</table>
)
}
}
Another option:
import React from 'react';
let id = 0;
function createData(option, type) {
id += 1;
return { id, option, type };
}
let rows = [
createData('Setting One', 'Private'),
createData('Setting Two', 'Public'),
createData('Setting Three', 'Group'),
createData('Setting Four', 'Private'),
];
export default class MyTable extends React.Component {
render() {
return(
<table>
{rows.map(row => (
<tr key={row.id}>
<td>{row.option}</td>
<td>{row.type}</td>
</tr>
))}
</table>
)
}
}
See: https://material-ui.com/demos/tables/
somthing like this :
<table id="simple-board">
<tbody>
{this.props.yourData.slice(0,this.state.size).map((item , index)=>{
return(
<tr key={index} id={`row${index}`}>
{item.felanBisar.map((item2,index2)=>{
return(
<td id={`cell${index}-{index2}`}></td>
);
})}
</tr>
);
})}
</tbody>
</table>
private getRows(): any {
let rows: any[] = [];
if (this.state.data)
this.state.data.map((element) => {
rows.push(<tr>
<td style={{border: '1px solid black'}}>
{element.title}
</td>
</tr>);
});
return rows;
}
render( ) {
return <div>
<table style={{border: '1px solid black'}}>
{this.getRows()}
</table>
</div>
}
Building the table from an array with a function, and rows with alternate bgColor:
function buildTable(arr){
const rows = arr.map((row,i) => {
return <tr style={{backgroundColor: i%2 ? '#F0FFF2':'white'}} key={i}>
<td>{row[0]}</td><td>{row[1]}</td>
</tr>
})
return <table><tbody>{rows}</tbody></table>
}
const data = [["FieldA", "aaa"],["FieldB", "bbb"],["FieldC", "ccc"]];
buildTable(data);

Categories