I am trying to render a table with dynamic content consisting of array of objects with Show More/ Show Less functionality.
While I am able to display the dynamic content, I am not able to bring in Show More/ Show less toggle. Basically Show More should show up when the number of items are greater than 3 and it should append the rest of items to the first three items. Show Less should be able to hide the items and show only first three
Help would be appreciated.
Sandbox: https://codesandbox.io/s/react-basic-class-component-3kpp5?file=/src/Table.js
Approach that I have tried
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;
From your code, I added a state named showLess to manage how the table should be displayed
import * as React from "react";
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
showLess: true
}
}
renderTableData = () => {
return this.props.data.map((item, index) => {
// If it's show less, then it should show only 3 rows, the others we will return null
if (this.state.showLess && index > 2) return null;
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>;
});
};
toggleShowLess = () => {
this.setState(prevState => ({
showLess: !prevState.showLess
}));
}
render() {
return (
<div>
<table>
<tbody>
<tr>{this.renderTableHeader()}</tr>
{this.renderTableData()}
<a href="#" onClick={this.toggleShowLess}>
{this.state.showLess ? 'Show More' : 'Show Less'}
</a>
</tbody>
</table>
</div>
);
}
}
export default Table;
added and handled this state :
this.state = {
showMore:false,
showing:DEFAULT_NUMBER_OF_ELEMS_TO_SHOW
}
import * as React from "react";
const DEFAULT_NUMBER_OF_ELEMS_TO_SHOW = 3;
class Table extends React.Component {
constructor(props){
super(props);
this.state = {
showMore:false,
showing:DEFAULT_NUMBER_OF_ELEMS_TO_SHOW
}
}
renderTableData = () => {
const {data} = this.props;
const {showing} = this.state;
let out = []
for(let i = 0; i<showing;i+=1){
const { name, value } = data[i];
out.push(( <tr key={i}>
<td>{name}</td>
<td>{value}</td>
</tr>))
}
return out;
};
renderTableHeader = () => {
let header = Object.keys(this.props.data[0]);
return header.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>;
});
};
setShownTableData = () =>{
const {showing,showMore} = this.state;;
const {data} = this.props;
this.setState({showMore:!showMore,
showing: showing === DEFAULT_NUMBER_OF_ELEMS_TO_SHOW ? data.length:DEFAULT_NUMBER_OF_ELEMS_TO_SHOW});
}
render() {
return (
<div>
<table>
<tbody>
<tr>{this.renderTableHeader()}</tr>
{this.renderTableData()}
</tbody>
</table>
<div onClick={()=>this.setShownTableData()}>{this.state.showMore ? "Show more":"Show Less"}</div>
</div>
);
}
}
export default Table;
https://codesandbox.io/s/react-basic-class-component-dp21g?file=/src/Table.js
Related
I would like to know how to create dynamic row with datepicker as a field on button click.
Need to create row dynamically on button click, row is generated on click.
But Datepicker not working.
But I need to add one field as Datepicker
How to do it in reactjs
I have DynamicTable component which creates row with fields, used in Home
import DynamicTable from "./../../../components/DynamicTable";
import DatePicker from "react-date-picker";
class Home exends React.PureComponent{
constructor(props){
super(props);
this.state={
additionalFields: [
{ date: "", id: 0 }
]
}
handleAddRow = e => {
if (e) e.preventDefault();
const rowLength = this.state.additionalFields.length;
let lastele = this.state.additionalFields[rowLength - 1];
const item = {
date: "",
id: lastele.id + 1
};
this.setState(
{
additionalFields: [...this.state.additionalFields, item]
}
);
};
render() {
return( <DynamicTable
columns={[
{ dataFieldId: "date", label: "Date", addRow: true },
{ dataFieldId: "AD", label: "" }
]}
rows={this.state.additionalFields}
addRow={this.handleAddRow}
/>);
}
}
}
DynamicTable Component
import React, { Component } from "react";
import DatePicker from "react-date-picker";
class DynamicTable extends Component {
constructor(props) {
super(props);
this.state = {
date: new Date();
};
}
handleDatePicker = (value, name, field, row) =>{
//this.props.handleInputChange(value, field, row);
this.setState({ [name] : value });
}
renderRowData = (column, row, col, index, rowId, rowleng) => {
if (column.addRow) {
return (
<td key={`tableview-td-${rowId}-${index}`}>
{column.dataFieldId==="date" ?
<DatePicker
locale = "en-GB"
className="datepicker"
name={"date_"+rowId}
onChange={(e)=>this.handleDatePicker(e, "date_"+rowId , column.dataFieldId, row)}
value={this.state.date}
/>
:"Loading"}
</td>
);
}
tableHeaders = () => (
<thead>
<tr>
{this.props.columns.map((column, index) => {
return <th key={`tableview-th-${index}`}>{column.label}</th>;
})}
</tr>
</thead>
);
tableBody = () => {
var rowleng = this.props.rows ? this.props.rows.length : 0;
return (
<tbody>
{this.props.rows.map(row => {
let index = row.id;
const rowId = `row_${index}`;
return (
<tr key={`tableview-tr-inner-${index}`} id={rowId}>
{this.props.columns.map((column, index) => {
const col = column.dataFieldId.replace(/\s/g, "");
return this.renderRowData(
column, row, col, index, rowId, rowleng
);
})}
</tr>
);
})}
</tbody>
);
};
renderRowData = (column, row, col, index, rowId, rowleng) => {
if (column.addRow) {
return (
<td key={`tableview-td-${rowId}-${index}`}>
<input
type="text"
defaultValue={row[column.dataFieldId]}
placeholder={
column.dataFieldId.charAt(0).toUpperCase() +
column.dataFieldId.slice(1)
}
/>
</td>
);
}
if (col === "AD") {
return (
<td key={`tableview-td-${rowId}-${index}`}>
<img
className="addBtn1"
onClick={this.props.addRow}
src={"/assets/icons/ic_add_blue.png"}
/>
</td>
);
}
return <td key={`tableview-td-${rowId}-${index}`}>{row[col]}</td>;
};
render() {
return (
<React.Fragment>
<div className="dynamicTable">
<table>
{this.tableHeaders()}
{this.tableBody()}
</table>
</div>
</React.Fragment>
);
}
}
export default DynamicTable;
You can pass configured date picker to your child component
state = {
date: new Date(),
}
onChange = date => this.setState({ date })
<DatePicker
onChange={this.onChange}
value={this.state.date}
/>
Im creating a table from JSON data, and main problem is I can't use object keys to map it.
My json something like this:
[{
key: 'val',
key2: 'val',
key3: 'val'
},
{
key: 'val',
key2: 'val',
key3: 'val'
}]
Here is how i implemented header with columns:
class Table extends Component {
render() {
const header = this.props.data.slice(0, 1);
return (<table>
<thead>
<TableHead children={header}/>
</thead>
<tbody>
<TableBody children={this.props.data}/>
</tbody>
</table>)
}
}
export default Table;
class TableHead extends Component {
render() {
return (
<tr>
{this.props.children.map((header) => {
return Object.keys(header).map((el) => {
return <th>{el}</th>
})
})}
</tr>
)
}
}
export default TableHead;
But I can't understand how to map my table body iterating over objects...
I sliced my JSON for header, but I can't do this with data, and my table looks like
class TableBody extends Component {
render() {
const row = this.props.children.map((row) => {
return Object.values(row).map((el,i) => {
if (i%Object.values(row).length===0) {
return <tr><td>{el}</td></tr>
}else{
return <td>{el}</td>
}
})
});
return (
<tbody>
{row}
</tbody>
)
}
}
export default TableBody;
I would extract the keys and re-use when mapping over the rows for the TableBody.
Something like
class Table extends Component {
render() {
const { data } = this.props;
const columns = Object.keys(data[0]);
return (
<table>
<thead>
<TableHead columns={columns} />
</thead>
<tbody>
<TableBody columns={columns} data={data} />
</tbody>
</table>
);
}
}
class TableHead extends Component {
render() {
const { columns } = this.props;
return (
<tr>
{columns.map(header => {
return <th>{header}</th>;
})}
</tr>
);
}
}
class TableBody extends Component {
render() {
const { columns, data } = this.props;
return data.map(row => (
<tr>
{columns.map(cell => (
<td>{row[cell]}</td>
))}
</tr>
));
}
}
Have to display the items via web API. I got the response successfully and displayed the items. But I don't know how to delete the item from the list.
Displayed the delete icon to every item, but I am not able to delete the item from the state. What did I wrong? Please help me out.
import React from 'react';
class App extends React.Component {
constructor() {
super();
this.state = {
posts: []
}
this.UserList = this.UserList.bind(this);
this.setStateHandler = this.setStateHandler.bind(this);
this.setDeleteHandler = this.setDeleteHandler.bind(this);
}
componentDidMount() {
this.UserList();
}
setStateHandler() {
alert(0)
var item = {
"url": "http://www.imdb.com/title/tt4937812/",
"title": "PAMMAL K SAMBANTHAM",
"imdb_id": "tt4937812",
"type": "Film",
"year": "2015"
};
var myArray = this.state.posts;
myArray.push(item)
console.log(myArray)
this.setState({posts: myArray})
};
setDeleteHandler() {
alert("idris")
};
UserList() {
alert(1)
fetch('http://www.theimdbapi.org/api/person?person_id=nm0352032').then(response => {
return response.json();
}).then(data => {
const posts = data.filmography.soundtrack;
this.setState({posts});
});
}
render() {
return (
<div>
<div>
{
this.state.posts.map((item, i) => {
return <Content item={item} key={i}/>
})
}
</div>
<button onClick = {this.setStateHandler}>SET STATE</button>
</div>
);
}
}
class Content extends React.Component {
render() {
return (
<div>
<table>
<tbody>
<tr>
<td>{this.props.item.title}</td>
<td>{this.props.item.type}</td>
<td>{this.props.item.type}</td>
<td><button onClick = {this.setDeleteHandler}>Delete</button></td>
</tr>
</tbody>
</table>
</div>
);
}
}
export default App;
Can anyone please help on this?
Thank you.
Make your setDeleteHandler like this
setDeleteHandler(index) {
return () => {
let posts = [...this.state.posts];
posts.splice(index,1);
this.setState({posts});
}
};
Then pass this function to your content component like this
this.state.posts.map((item, i) => {
return <Content item={item} key={i} deleteHandler={this.setDeleteHandler(i)}/>
})
And then in your content component call this function like this
<td><button onClick = {this.props.deleteHandler}>Delete</button></td>
I am trying to edit a row of table containing person objects. My idea is to show the table and after clicking on the row to edit, the row changes to 2 textBoxes (Nameand surnameto edit) and a button to confirm edit operation. When I run the program the table shows 4 times its size(I have 4 rows) and when I click on a random row disappears 4 rows and the table becomes 3 times its size(12 rows).
What is causing the fail? Thanks for your time
The code:
class EditPersons extends React.Component {
constructor(props) {
super(props);
this.state = {
editing: null
};
this.editPerson = this.editPerson.bind(this);
}
componentDidMount() {
this.props.fetchData('http://localhost:9536/persons/');
}
editPerson(person) {
this.setState(
{ editing: person.PersonId }
);
}
renderPersonOrEdit(person) {
if (this.state.editing === person.PersonId) {
console.log('editing: ' + person.PersonId); //test ok
//Here comes the 2 textBoxes and the edit-button
}
else {
return (
<tbody>
{this.props.persons.map((person, i) => {
return(
<tr key={i}>
<td onClick={()=>this.editPerson.(person)}><Link>{person.Name}<Link></td>
<td>{person.Surname}</td>
</tr>
);
})}
</tbody>
);
}
}
render() {
if (this.props.hasErrored) {
return <p>Downloading has failed!</p>;
}
if (this.props.isLoading) {
return <p>Downloading…</p>;
}
return (
<div>
<table id="myTable">
<thead>
<tr>
<th>Person name</th>
<th>Person Surname</th>
</tr>
</thead>
{this.props.persons.map((person) => {
return this.renderPersonOrEdit(person);
})}
</table>
<Link to="/project" className="btn btn-primary btn-xs" style={{marginTop: 20}}>Back</Link>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
personss: state.persons,
hasErrored: state.personsHasErrored,
isLoading: state.personsIsLoading
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: (url) => dispatch(personsFetchData(url))
};
};
EditPersons.propTypes = {
fetchData: PropTypes.func.isRequired,
persons: PropTypes.array.isRequired,
hasErrored: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired
};
export default connect(mapStateToProps, mapDispatchToProps)(EditPersons);
In your render() function you are already mapping over the elements and then in the renderPersonOrEdit you are doing the same, change your code to map at only the render function
class EditPersons extends React.Component {
constructor(props) {
super(props);
this.state = {
editing: null
};
this.editPerson = this.editPerson.bind(this);
}
componentDidMount() {
this.props.fetchData('http://localhost:9536/persons/');
}
editPerson(person) {
this.setState(
{ editing: person.PersonId }
);
}
renderPersonOrEdit() {
if (this.state.editing !== null) {
console.log('editing: ' + person.PersonId); //test ok
//Here comes the 2 textBoxes and the edit-button
//set editing to null after editing the contents
}
else {
return <tbody>
{this.props.persons.map((person) => {
return (
<tr key={person.PersonId}>
<td onClick={()=>this.editPerson.(person)}><Link>{person.Name}<Link></td>
<td>{person.Surname}</td>
</tr>
);
})}
</tbody>
}
}
render() {
if (this.props.hasErrored) {
return <p>Downloading has failed!</p>;
}
if (this.props.isLoading) {
return <p>Downloading…</p>;
}
return (
<div>
<table id="myTable">
<thead>
<tr>
<th>Person name</th>
<th>Person Surname</th>
</tr>
</thead>
{this.renderPersonOrEdit()}
</table>
<Link to="/project" className="btn btn-primary btn-xs" style={{marginTop: 20}}>Back</Link>
</div>
);
}
}
Try show in table my array. Array get from AJAX request in Action. I'm using Redux
class IncomeProfile extends Component {
constructor(props) {
super(props)
}
componentDidMount() {
this.props.IncomeListProfile();
}
render() {
var elems = this.props.items.course_list;
console.log(elems);
return (
<div>
<table>
{elems.map((item) => (
<tr key={item.course_id}>
<td>{item.name}</td>
</tr>
))}
</table>
</div>
)
}
}
const mapDispatchToProps = function(dispatch) {
return {
IncomeListProfile: () => dispatch(IncomeProfileList())
}
}
const mapStateToProps = function(state) {
var mystore = state.toArray();
//console.log(mystore[6]);
return {
items: mystore[6]
};
}
export default connect(mapStateToProps, mapDispatchToProps)(IncomeProfile);
Console.log first print "undefined" then it print this:
Try add condition in render method if (elems) { } not helps
Make use of the following
var elems = this.props.items['course_list'];
var copy = Object.assign({}, elems);
console.log(elems.course_list);