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}
/>
Related
I am trying to sort the table data based on the field selected from the dropdown. It should alternatively sort between ascending or descending whenever the same field is clicked. I am maintaining a sort object that decides which field is selected and what is the sort order. Then it is sent to lodash orderBy with the field and the order. It does not work
This is what I have tried. Can some one tell me what I am doing wrong. Help is really appreciated.
https://codesandbox.io/s/simple-react-class-component-1n3f9?file=/src/index.js:0-3000
import React from "react";
import ReactDOM from "react-dom";
import "semantic-ui-css/semantic.min.css";
import { Dropdown } from "semantic-ui-react";
import moment from "moment";
import orderby from "lodash.orderby";
const options = [
{ key: 1, text: "Name", value: "name", icon: "sort" },
{ key: 2, text: "Time", value: "time", icon: "sort" },
{ key: 3, text: "Type", value: "type", icon: "sort" }
];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [],
original: [],
sortObject: { field: "", order: "" }
};
}
componentDidMount() {
let list = [
{
name: "namev1",
time: 1583295463213,
type: 14
},
{
name: "namea2",
time: 1582885423296,
type: 15
},
{
name: "namea3",
time: 1581295463213,
type: 16
}
];
this.setState({ list, original: list });
}
handleSearch = e => {
let searchInput = e.target.value;
let filteredData = this.state.original.filter(value => {
return (
value.name.toLowerCase().includes(searchInput.toLowerCase()) ||
value.type.toString().includes(searchInput.toString())
);
});
this.setState({ list: filteredData });
};
formSortObject = fieldName => {
let { sortObject } = this.state;
if (!sortObject.field || sortObject.field !== fieldName) {
Object.assign(sortObject, {
field: fieldName,
order: "asc"
});
return sortObject;
} else if (sortObject.field === fieldName) {
Object.assign(sortObject, {
...sortObject,
order: sortObject.order === "desc" ? "asc" : "desc"
});
return sortObject;
}
};
handleSort = (e, data) => {
let dropdDownValue = data.value;
let currentField = this.formSortObject(dropdDownValue);
let result = orderby(
this.state.list,
currentField.field,
currentField.order
);
this.setState({ list: result });
};
render() {
return (
<>
Search: <input type="text" onChange={this.handleSearch} />
<Dropdown text="Sort By" options={options} onChange={this.handleSort} />
<h1>List</h1>
<table>
<tbody>
{this.state.list.map((item, index) => (
<tr key={index}>
<td>
<p>{index + 1}</p>
</td>
<td>
<p>{item.name}</p>
</td>
<td>
<p>{moment().diff(item.time, "days")}</p>
</td>
<td>
<p>{item.type}</p>
</td>
</tr>
))}
</tbody>
</table>
</>
);
}
}
Your code isnt's working as you spected because you are calling the handleSort function only when the value of the select change (see the onChange of your <Dropdown />).
What you need is the function executed when an option is clicked.
I searched for the documentation of the library you are using and I came to the conclusion that what you need is this.
<Dropdown text='Sort By'>
<Dropdown.Menu>
{options.map(item=>
<Dropdown.Item text={item.text} value={item.value} icon={item.icon} key={item.key} onClick={this.handleSort}/>
)}
</Dropdown.Menu>
</Dropdown>
I tried it in your codesandbox and it works perfectly!
I hope it helps!
I'm developing an app using React. However, facing some issue with reflecting the {column.label} at column header field. The code works with any plain text but not for inputs in {}.
Below is the code for reference.
filename: tableHeader.jsx:
import React, { Component } from "react";
class TableHeader extends Component {
raiseSort = (path) => {
const sortColumn = { ...this.props.sortColumn };
if (sortColumn.path === path)
sortColumn.order = sortColumn.order === "asc" ? "desc" : "asc";
else {
sortColumn.path = path;
sortColumn.order = "asc";
}
this.props.onSort(sortColumn);
};
render() {
return (
<thead>
<tr>
{this.props.columns.map((column) => (
<th
key={column.path || column.key}
onClick={() => this.raiseSort(column.path)}
>
{column.label}
</th>
))}
</tr>
</thead>
);
}
}
export default TableHeader;
filename: moviesTable.jsx:
import React, { Component } from "react";
import Like from "./common/like";
import TableHeader from "./common/tableHeader";
class MoviesTable extends Component {
columns = [
{ path: "title", lable: "Title" },
{ path: "genre.name", lable: "Genre" },
{ path: "numberInStuck", lable: "Stock" },
{ path: "dailyRentalRate", lable: "Rate" },
{ key: "Like" },
{ key: "Delete" },
];
render() {
const { movies, onDelete, onLike, onSort, sortColumn } = this.props;
return (
<table className="table">
<TableHeader
columns={this.columns}
sortColumn={sortColumn}
onSort={onSort}
/>
<tbody>
{movies.map((movie) => (
<tr key={movie._id}>
<td> {movie.title} </td>
<td> {movie.genre.name} </td>
<td> {movie.numberInStock} </td>
<td> {movie.dailyRentalRate} </td>
<td>
<Like liked={movie.liked} onClick={() => onLike(movie)} />
</td>
<td>
<button
onClick={() => onDelete(movie)}
className="btn btn-danger btn-sm"
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
);
}
}
export default MoviesTable;
fileName: movie.jsx:
import React, { Component } from "react";
import { getMovies } from "../services/fakeMovieService";
import { getGenres } from "../services/fakeGenreService";
import Pagination from "./common/pagination";
import { paginate } from "../utils/paginate";
import ListGorup from "./common/listGroup";
import MoviesTable from "./moviesTable";
import _ from "lodash";
class Movies extends Component {
state = {
movies: [],
genres: [],
pageSize: 4,
currentPage: 1,
sortColumn: { path: "title", order: "asc" },
};
componentDidMount() {
const genres = [{ _id: "", name: "All Genres" }, ...getGenres()];
this.setState({ movies: getMovies(), genres });
}
handleDelete = (movie) => {
const movies = this.state.movies.filter((m) => m._id !== movie._id);
this.setState({ movies });
};
handleLike = (movie) => {
const movies = [...this.state.movies];
const index = movies.indexOf(movie);
movies[index] = { ...movie };
movies[index].liked = !movies[index].liked;
this.setState({ movies });
};
handlePageChange = (page) => {
this.setState({ currentPage: page });
};
handleGenreSelect = (genre) => {
this.setState({ selectedGenre: genre, currentPage: 1 });
};
handleSort = (sortColumn) => {
this.setState({ sortColumn });
};
render() {
const { length: count } = this.state.movies;
const {
pageSize,
currentPage,
movies: allMovies,
selectedGenre,
sortColumn,
} = this.state;
if (count === 0) return <p>No movies in the DB..</p>;
const filtered =
selectedGenre && selectedGenre._id
? allMovies.filter((m) => m.genre._id === selectedGenre._id)
: allMovies;
const sorted = _.orderBy(filtered, [sortColumn.path], [sortColumn.order]);
const movies = paginate(sorted, currentPage, pageSize);
return (
<div>
<div className="row">
<div className="col-3">
<ListGorup
items={this.state.genres}
onItemSelect={this.handleGenreSelect}
selectedItem={this.state.selectedGenre}
/>
</div>
<div className="col">
<p>Showing {filtered.length} movies in the DB. </p>
<MoviesTable
movies={movies}
onLike={this.handleLike}
onDelete={this.handleDelete}
onSort={this.handleSort}
sortColumn={sortColumn}
/>
<Pagination
itemsCount={filtered.length}
onPageChange={this.handlePageChange}
pageSize={pageSize}
currentPage={currentPage}
/>
</div>
</div>
</div>
);
}
}
export default Movies;
The below code from file1(tableHeader.jsx) is responsible to give the column name. Not sure, why it's not coming.
<thead>
<tr>
{this.props.columns.map((column) => (
<th
key={column.path || column.key}
onClick={() => this.raiseSort(column.path)}
>
{column.label}
</th>
))}
</tr>
</thead>
Looking for to hearing from you.
Your columns list only contains objects with the key lable (misspelled). It should be label:
columns = [
{ path: "title", /* not lable ! */ label: "Title" },
{ path: "genre.name", label: "Genre" },
{ path: "numberInStuck", label: "Stock" },
{ path: "dailyRentalRate", label: "Rate" },
{ key: "Like" },
{ key: "Delete" },
];
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
I want to get the following effect: click button -> call function toggle -> save the index of the clicked item in the state this.state.index -> display edit form ---> get the data of the clicked element to the form -> edit data - > save
When trying to call the toggle function and write the index in a state, the index does not save. After saving the index in the state I would like to use it and have access to the clicked element in todo = this.state.todos [this.props.index]. I send the data of the clicked todo in the form attribute. In the form, he refers to this via value = this.props.todo.date. I'm using the date-picker-react library. Could someone guide me?
App
class App extends Component {
constructor() {
super();
this.state = {
todos: [],
index: null,
editing: false
};
}
update = (propertyName) => (event) => {
const { todo } = this.state;
const newTodo = {
...todo,
[propertyName]: event.target.value
};
this.setState({ todo: newTodo });
}
toggle = (index) => {
this.setState({
editing: !this.state.editing,
index: index
})
}
createTodo = (todo) => {
const new = this.state.todos.slice();
new.push(todo);
this.setState({todos: new});
}
render () {
return (
<ul>
{
this.state.todos
.map(index => {
<Todo
key= {index}
index = {this.state.index}
toggle = {this.toggle}
todo={this.state.todos[index]}
editing = {this.state.editing}
update = {this.update}
/>
})
}
</ul>
);
}
}
export default App;
todo/Todo
class Todo extends Component {
render() {
if (this.props.isEditing) {
return (
<EditForm
todo = {this.props.todos[this.props.index]}
update = {this.props.update}
/>
)
}
return (
<li>
<div>
{this.props.todo.date}
</div>
<div>
{this.props.todo.description}
</div>
<button onClick={() => this.props.toggle(index)}></button>
</li>
)
}
}
export default Todo;
editForm/EditForm
class EditForm extends Component {
constructor(){
super();
this.state = {
startDate: new Date()
}
}
todo(e) {
event.preventDefault();
const todo = {
date: this.state.startDate,
description: this.desc.value
}
this.props.addTodo(todo);
}
handleChange = (date) => {
this.setState({
startDate: date
});
}
render() {
return (
<form onSubmit={(e) => this.todo(e)}>
<DatePicker
selected={this.state.startDate}
onChange={this.update('date')} value=
{this.state.todo.date}
showTimeSelect
timeFormat="HH:mm"
value={todo.date}
dateFormat="yy-MM-dd, hh:mm"
timeCaption="time"
/>
<textarea ref={(input) => this.description = input} value=
{todo.description} onChange={this.update('description')}
value={this.state.todo.description}></textarea>
<button type="submit">Save</button>
</form>
)
}
}
export default EditForm;
Let each todo manage it's state, on edit it will show the form for that todo.
class Todo extends Component {
state = {
isEditing: false,
startDate: new Date(),
description: '',
}
setEditing = () => {
this.setState({
isEditing: !this.state.isEditing
})
}
handleChange = (date) => {
this.setState({
startDate: date
});
}
handleDescription = (evt) => {
this.setState({
description: evt.target.value
})
}
formatDate = () => {
const d = this.state.startDate;
return d.getFullYear().toString().substring(2) + "-" +
("00" + (d.getMonth() + 1)).slice(-2) + "-" +
("00" + d.getDate()).slice(-2) + ", " +
("00" + d.getHours()).slice(-2) + ":" +
("00" + d.getMinutes()).slice(-2)
}
onSave = () => {
const { description } = this.state;
this.props.update(this.props.index, { description, date: this.formatDate() });
this.setState({
isEditing: false
})
}
componentDidMount = () => {
const { todo } = this.props;
this.setState({
description: todo.description,
startDate: new Date(todo.date)
})
}
render() {
return (
<div>
{this.state.isEditing
? (<EditForm
handleChange={this.handleChange}
description={this.state.description}
startDate={this.state.startDate}
handleDescription={this.handleDescription}
onSave={this.onSave}
/>)
: (
<li>
<div>
{this.props.todo.date}
</div>
<div>
{this.props.todo.description}
</div>
<button onClick={() => this.setEditing()}>Edit</button>
</li>
)
}
</div>
)
}
}
Edit form
class EditForm extends Component {
render() {
return (
<div>
<DatePicker
selected={this.props.startDate}
onChange={this.props.handleChange}
showTimeSelect
timeFormat="HH:mm"
value={this.props.startDate}
dateFormat="yy-MM-dd, hh:mm"
timeCaption="time"
/>
<textarea onChange={(e) => this.props.handleDescription(e)} value={this.props.description}></textarea>
<button onClick={this.props.onSave} type="submit">Save</button>
</div>
)
}
}
Demo
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>
));
}
}