I want to achieve expandable row functionality for table.
Let's assume we have table with task names and task complexity. When you click on one task the description of task is shown below. I try to do it this way with ReactJS (in render method):
if (selectedTask === task.id) {
return [
<tr>
<td>{task.name}</td>
<td>{task.complexity}</td>
</tr>,
<tr>
<td colSpan="2">{task.description}</td>
</tr>
];
} else {
return <tr>
<td>{task.name}</td>
<td>{task.complexity}</td>
</tr>;
}
And it doesn't work. It says:
A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object
I tried also to wrap 2 rows in a div but I get wrong rendering.
Please, suggest correct solution.
The render() method on a React component must always return a single element. No exceptions.
In your case, I would suggest wrapping everything inside a tbody element. You can have as many of those as you want in a table without disrupting your row structure, and then you'll always return one element inside render().
if (selectedTask === task.id) {
return (
<tbody>
<tr>
<td>{task.name}</td>
<td>{task.complexity}</td>
</tr>,
<tr>
<td colSpan="2">{task.description}</td>
</tr>
</tbody>
);
} else {
return (
<tbody>
<tr>
<td>{task.name}</td>
<td>{task.complexity}</td>
</tr>
</tbody>
);
}
Related
I'm trying to display data in the table, but for some reasons appears empty fields. I think the issue is that firstly is looking deaths - fills the elements and cases becomes empty. Tried to do in many different ways. In some ways all data are just pulled in one column, in other ways the data just go in rows. Tryied to use lodash as well.
Current code:
<table className="table">
<tr>
<th>Cases</th>
<th>Deaths</th>
</tr>
{_.map(countryData, (item, key) => {
if (item.indicator === "cases") {
return (
<tr>
<td>{item.weekly_count}</td>
<td></td>
</tr>
);
} else {
return (
<tr>
<td>{item.weekly_count}</td>
</tr>
);
}
})}
</table>
Also tried in this way(still empty fields):
{countryData.map((value, key) => {
return (
<tr>
<td>
{value.indicator === "cases" ? value.weekly_count : null}
</td>
<td>
{value.indicator === "deaths" ? value.weekly_count : null}
</td>
</tr>
);
})}
My data from console.log:
Data
Expected result: expected result
Example of issue: Issue
countryData: This data comes after selecting the specific country
The map function is working just fine. The problem is with the object countryData. The object currently does not contain an array. Please make sure the object looks like:
countryData = [{indicator:"cases", weekly_count: xx}, {indicator:"deaths", weekly_count: yy}.....]
I have only used two fields inside each object, you may add more objects according to your requirement.
In order to view cases and deaths as separate columns, please add an empty cell before displaying "deaths".
{_.map(countryData, (item, key) => {
if (item.indicator === "cases") {
return (
<tr>
<td>{item.weekly_count}</td>
<td></td>
</tr>
);
} else {
return (
<tr>
<td></td>
<td>{item.weekly_count}</td>
</tr>
);
}
})}
I'm trying to solve this problem. First step is create a table after clicking on first button. Later the table is creating and one row has another button which should starting another function play() with arguments. For this example I just want to display ready in console but this is undefined. When I call this function by simply console.log outside the function then is working correctly. What is wrong with calling function play by button?
<table class="table">
<tr>
<th>Team A</th>
<th>Team B</th>
<th></th>
</tr>
</table>
<button onclick="create()">Create next Match</button>
const table = document.querySelector('.table');
const teams=[{
name: 'Real',
point: 10
},
{
name: 'Barca',
point: 20
}]
function create(){
table.innerHTML += `
<tr id="r1m1">
<td id="host">${teams[0].name}</td>
<td id="guest">${teams[1].name}</td>
<td id="host_score"></td>
<td id="guest_score"></td>
<td><button onclick="play(${teams[0]},${teams[1]})">Play</button></td>
</tr>`
}
function play(a,b){
console.log("ready");
}
console.log(play(teams[0],teams[1]))
You are mashing strings together, so teams[0] gets converted to [object Object] which not only doesn't contain the data you want, but also isn't syntactically valid JavaScript.
You might be able to manage with onclick="play(teams[0],teams[1])" but scope might trip you up (as teams and play must be globals, which are best avoided in general).
In general, your should use DOM methods like createElement, addEventListener and appendChild instead.
Don't use template substitution, just refer to the variables in directly in the onclick, just like when you call play() in the top-level code.
function create(){
table.innerHTML += `
<tr id="r1m1">
<td id="host">${teams[0].name}</td>
<td id="guest">${teams[1].name}</td>
<td id="host_score"></td>
<td id="guest_score"></td>
<td><button onclick="play(teams[0],teams[1])">Play</button></td>
</tr>`
}
I have a Table which when you click a td tag that is an plusbutton it should show the details about that row. Something like this:
Right now I am just testing it like this:
props.info.map((l, i) => {
return (
<tr>
<td>{i + 1}</td>
<td>{l.UserId}</td>
<td onClick={props.onShowInfoDetails}>
<MenuPlusButton /></td>
{props.showInfoDetails && (
<DetailsTable />
)
}
</tr>
)
})
where the DetailsTable is the thing i want to render onClick
export const DetailsTable = (props: Props) => {
return (
<tr>
<td>Hello</td>
</tr>
)
}
There is two problems with this. First the DetailsTable renders to the right of the rest of the content and not under it like in the picture. second problem is that when I click it all table rows show the hello not just the one that I clicked. Both of these I can't seem to figure out. The second problem I guess is because it says if props.showEntryDetails is true it renders the DetailsTable and the onClick sets it to true but how do I make it so it's only true for that row that I clicked?
I am trying to create a Table using React and React-Bootstrap that has a custom number of table rows. The table is supposed to store data about player statistics of a certain video game, and based on the video game the statistics may change, thus the number of rows and titles of these rows must be able to dynamically change as well. I wanted to create an array in the state that held the list of current statistics, then map this array to a element using the map function and render the table. However, after trying several approaches I can't get any of the custom input to render. Below is the code :
Class Structure
class Statistics extends Component {
constructor(props) {
super(props)
this.state = {
game: '',
player_names: [],
positions: [],
stat_categories: [
'kills',
'deaths',
'assists'
]
}
}
renderTableRows(array) {
return (
<tr>
<th> NAME </th>
<th> TEAM </th>
<th> POSITION </th>
{ array.map(item => {
console.log(item)
<th key={item}> {item} </th>
})
}
</tr>
)
}
render() {
const columnLength = this.state.player_names.length
const statCols = this.state.stat_categories
return (
<div>
<MyNav url={this.props.location.pathname} />
<Table responsive striped bordered hover>
<thead>
{ this.renderTableRows(statCols) }
</thead>
</Table>
</div>
)
}
}
The console also properly logs the data in state (kills, deaths, assists) -- so the issue is when rendering the element. Any help would be appreciated!
You have no return statement in your map function, inside of renderTableRows.
When using ES6 arrow functions, you can either:
Return data directly without a return statement
(args) => (returnedData);
Or add some logic instead of just returning directly,
(args) => {
// Logic here
return returnedData
}
In the second case you'll need a return statement, because you are logging, if you choose to remove logging, go the first way.
Also, please post the code directly in your question, as using an image makes it less readable and not indexed by search engines.
You have to render each item in separate trs, not as a series of ths
renderTableCols(array) {
return array.map(item => <th>{item}</th>)
}
renderTableColValues(item, cols) {
return cols.map(col => <td>{item[col]}</td>)
}
renderTableRows(array) {
return array.map(item =>
<tr>
<td>{item.name}</td>
<td>{item.team}</td>
<td>{item.position}</td>
{this.renderTableColValues(item, this.cols)}
</tr>
);
}
render() {
return (
<Table>
<thead>
<tr>
<th>NAME</th>
<th>TEAM</th>
<th>POSITION</th>
{this.renderTableCols(this.cols)}
</tr>
</thead>
<tbody>
{this.renderTableRows(items)}
</tbody>
</Table>
);
}
More on tables https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table
I will give you a similar answer of what youre encoutering but its kinda different approach with a excelent solution
So, you are trying to create a dynamic table but youre making table rows static, what i did was letting the table to receive arrays of head and data and then create as many rows or datas that are required.
heres the code
export function objectIntoTableData(object) {
return Object.values(object).map((data, index) => {
return <td key={index}>{data}</td>;
});
}
You must change this index to (value,index) => , thats just my use
tableRows(data) {
return data.map(value => {
return <tr key={value.index}>{objectIntoTableData(value)}</tr>;
});
}
<thead>
<tr>
{head.map((value, index) => {
return <th key={index}>{value}</th>;
})}
</tr>
</thead>
<tbody>{this.tableRows(data)}</tbody>
Rather use a id or index inside your object since the index callback of the map function, its unsafe to use for the keys.
<ReactTableUse
head={["#", "Cell1", "Cell2", "Cell3"]}
data={[{id:1, test:1},{id:2, test:2}]}
/>
Rules:
When your state changes, render method of a class based component will be called.
Question: Who will change the state? will it grow inside the component ? What is your problem ? your are not being able to render anything ? or statistics is not dynamically rendering ? if you want to change it dynamically , you need to change the state first.
A piece of my HTML code looks like this:
<tr data-tt-id="1">
<td>Parent</td>
</tr>
<tr data-tt-id="2" data-tt-parent-id="1">
<td>Child 1</td>
</tr>
<tr data-tt-id="4" data-tt-parent-id="2">
<td>Child 1's child</td>
</tr>
<tr data-tt-id="3" data-tt-parent-id="1">
<td>Child 2</td>
</tr>
<tr data-tt-id="5" data-tt-parent-id="3">
<td>Child 2's child</td>
</tr>
I'm selecting the parent which has data-tt-id="1" by using this:
$('tr[data-tt-parent-id="1"]');
But I want all of the children and children's children too, no matter how deep the tree might be.
As you can see data-tt-id is the child's unique ID and data-tt-parent-id is the ID which the child is appended to.
I was thinking about looping through each one of them, but I have no idea how I would achieve that.
How do I select all descendants for the tr that has data-tt-id set to "1"?
function getDescendants(el, curset) {
curset = curset || $(""); // default to empty set
var id = el.data('tt-id');
var children = $("tr[data-tt-parent-id="+id+"]");
if (children.length) {
curset = curset.add(children);
children.each(function() {
curset = curset.add(getDescendants($(this), curset));
});
}
return curset;
}
There's probably more idiomatic ways to write this without having to reassign curset = ... in several places, but this works.
DEMO