I created a search app where users can search movies and it will be shown in the table. However, I want a delete button in the last column in each movie row to delete a movie from the table. I'm being unable to do that. Can someone help me as to how to add that delete button in the last column? I've already created the deleteMovie action and reducers. I'm just not sure how to add it to the table. I tried to do as they told in the docs but it isn't working for me
import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { deleteMovie } from "../action/movieActions";
import "react-bootstrap-table-next/dist/react-bootstrap-table2.min.css";
import BootstrapTable from "react-bootstrap-table-next";
const MovieTable = ({ data, deleteMovie }) => {
console.log(data);
const columns = [
{
dataField: "movieId",
text: "ID",
sort: true
},
{
dataField: "name",
text: "Name",
sort: true
},
{
dataField: "year",
text: "Year",
sort: true
},
{
dataField: "Delete",
title: "delete",
text: "Delete",
events: {
onClick: () => deleteMovie(data.movieId)//tried this but didn't work
}
}
]
return (
<div className="col m4">
<BootstrapTable keyField="id" data={data} columns={columns} />
</div>
);
};
MovieTable.propTypes = {
deleteMovie: PropTypes.func.isRequired
};
export default connect(
null,
{ deleteMovie }
)(MovieTable);
That way you are adding click event on column.
First you have to create a hook or a component that returns a button (The delete button) and then pass as parameter to that hook or component the id of the product.
The hook/component you will create has to be called on the data array!
The selector data.movieId is not valid, the data variable is an object containing an array. Select the movieId like this:
{
dataField: "movieId",
formatter: (rowContent, row) => {
return (
<button onClick={() => deleteMovie(data[0].movieId)}>delete</button>
);
}
}
Related
I have a table on my react website using Tabulator. For some reason, the column headings would not show even though I have all the titles setup. My guess is that maybe cause I am setting the data again on componentDidUpdate() which is loading the data from redux.
Please help me on how to fix this.
The attached screenshot below shows how it is currently looking. I would want the column headings like "Name, ID" be shown on top.
import React, {Component} from 'react';
import {connect} from 'react-redux'
import {withRouter} from "react-router";
import 'react-tabulator/lib/styles.css'; // required styles
import 'react-tabulator/lib/css/tabulator_modern.css'; // theme
import Tabulator from "tabulator-tables";
class AllCoursesTabulator extends Component {
constructor(props) {
super(props);
this.el = React.createRef();
this.tabulator = null;
this.ref = null;
this.dataEditedFunc = this.dataEditedFunc.bind(this);
}
componentDidMount() {
let columns = [
{title: "ID", width: 150, field: "course_id"},
{title: "Name", field: "course_name"},
{title: "Section", field: "course_section"},
{title: "Instructor ID", field: "employee_id"},
{title: "Instructor Email", field: "employee_email", width: 250},
{
title: "Ilearn Video Service Requested",
field: "ilearn_video_service_requested",
hozAlign: "center",
formatter: "tickCross"
},
{title: "Ilearn Page ID", field: "ilearn_id"}
];
this.tabulator = new Tabulator(this.el, {
columns: columns,
layout: "fitColumns",
data: this.props.data,
reactiveData: true,
height: "500px",
cellEdited: this.dataEditedFunc
})
}
componentDidUpdate(prevProps, prevState, snapshot) {
this.tabulator.replaceData(this.props.data)
}
dataEditedFunc(cellData) {
//this function is to edit the cell on click
};
render() {
return (
<div className="emailTabulatorContainer">
<div ref={el => (this.el = el)}/>
</div>
)
}
}
function mapStateToProps({
globalsReducer,
coursesReducer
}, {props}) {
let data = []
let columns = []
let formatData = (course) => {
let ilearn_data = course.ilearn_page_id
if (ilearn_data != null) {
return {
course_id: course.course_gen_id,
course_name: course.course_name,
course_section: course.course_section,
employee_id: course.employee_id,
employee_email: course.course_instructor.employee_email,
ilearn_video_service_requested: course.ilearn_video_service_requested,
ilearn_id: course.ilearn_page_id.ilearn_page_id
}
}
}
if (coursesReducer !== undefined) {
Object.keys(coursesReducer).forEach(function (key) {
data.push(formatData(coursesReducer[key]))
});
}
return {
data,
columns,
semester: globalsReducer['currentSemester'],
coursesReducer
}
}
export default withRouter(connect(mapStateToProps)(AllCoursesTabulator))
Please tell me how to fix this. Thank you for your help.
Try to modify css? Just Like this
.tabulator-header .tabulator-headers.tabulator-col {
height: 40px !important; }
But, that's not a good solution.
After implementing the drag and drop feature on AG Grid table, I'm looking for a way to get the current state with the updated order/index of rows. My goal is to persist the table data after changing the order, but can't find the respective state of the current order.
I'd appreciate any help or any idea.
Sandbox demo and example code below
import React from "react";
import { AgGridReact } from "ag-grid-react";
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";
function App() {
const [gridApi, setGridApi] = React.useState(null);
const [gridColumnApi, setGridColumnApi] = React.useState(null);
const onGridReady = (params) => {
setGridApi(params.api);
setGridColumnApi(params.columnApi);
};
const defaultColDef = {
flex: 1,
editable: true
};
const columnDefs = [
{
headerName: "Name",
field: "name",
rowDrag: true
},
{ headerName: "stop", field: "stop" },
{
headerName: "duration",
field: "duration"
}
];
const rowData = React.useMemo(
() => [
{
name: "John",
stop: 10,
duration: 5
},
{
name: "David",
stop: 15,
duration: 8
},
{
name: "Dan",
stop: 20,
duration: 6
}
],
[]
);
return (
<div>
<h1 align="center">React-App</h1>
<div>
<div className="ag-theme-alpine" style={{ height: "700px" }}>
<AgGridReact
columnDefs={columnDefs}
rowData={rowData}
defaultColDef={defaultColDef}
onGridReady={onGridReady}
rowDragManaged={true}
></AgGridReact>
</div>
</div>
</div>
);
}
export default App;
You can get the order of the rows inside the grid by iterating over them using the Grid API method forEachNode:
API for Row Nodes
const rows = [];
gridApi.forEachNodeAfterFilterAndSort((node) => rows.push(node.data));
console.log(rows);
See this implemented in the following sample.
You're currently using managed dragging by passing rowManagedDragging={true}, which means the AgGridReact component is managing the row order state.
If you want to maintain row order state outside the component, you need to use Unmanaged Dragging.
Add a handler for onRowDragMove, and use the node and overIndex or overNode properties of the event to update your local event order state, and pass it to the AgGridReact component to re-render.
Take a look at this example from the docs
I am using community version of ag-grid in my project. I am trying add menu button in one of the cell of every row. on clicking of the menu button, there should be menu pop up, which will have Edit/delete/rename options and I need to fire event with row value when any item on menu is clicked.
I am trying to create a cell renderer which will display the button. menu will be hidden initially and on clicking of button, I am changing display using css class. I am seeing the css class is getting added correctly but the menu is still not visible. I checked in the console and it is hidden behind the table. I used position absolute and z-index at various place but ended up with no luck.
I can not use context menu or enterprise menu out of box as I am using community version. can you please help me here? also, is there any better way to achieve this result then let me know. Thanks a lot in advance.
var students = [
{value: 14, type: 'age'},
{value: 'female', type: 'gender'},
{value: "Happy", type: 'mood'},
{value: 21, type: 'age'},
{value: 'male', type: 'gender'},
{value: "Sad", type: 'mood'}
];
var columnDefs = [
{
headerName: "Value",
field: "value",
width: 100
},
{headerName: "Type", field: "type", width: 100},
{headerName: "Action", width: 100, cellRenderer: 'actionMenuRenderer' }
];
var gridOptions = {
columnDefs: columnDefs,
rowData: students,
onGridReady: function (params) {
params.api.sizeColumnsToFit();
},
components:{
actionMenuRenderer: ActionMenuCellRenderer
}
};
function ActionMenuCellRenderer() {
}
ActionMenuCellRenderer.prototype.init = function (params) {
this.eGui = document.createElement('div')
if (params.value !== "" || params.value !== undefined || params.value !== null) {
this.eGui.classList.add('menu');
this.eGui.innerHTML = this.getMenuMarkup();
this.actionBtn = this.eGui.querySelector(`.actionButton`);
this.menuWrapper = this.eGui.querySelector(`.menuWrapper`);
this.actionBtn.addEventListener('click', event => this.onActionBtnClick(event));
}
};
ActionMenuCellRenderer.prototype.getGui = function () {
return this.eGui;
};
ActionMenuCellRenderer.prototype.onActionBtnClick = function() {
alert('hey');
this.menuWrapper.classList.toggle('showMenu');
}
ActionMenuCellRenderer.prototype.getMenuMarkup = function () {
return `
<button type="button" class="actionButton">
menu
</button>
<div class="menuWrapper">
<a class="menuItem">
Edit
</a>
<a class="menuItem">
Delete
</a>
<a class="menuItem">
Duplicate
</a>
</div>
`;
}
My plnkr sample-
plnkr sample
The issue is due to the context menu also renders inside the ag-grid cell. So it does not matter how much z-index you give it can not display it outside the cell renderer div of the ag grid. The solution is we can use the library like Tippys which will render the menu outside the ag-grid main div which will fix the issue. Below is the sample code for react to show the menu on click of a button in ag-grid cell renderer.
There was nice blog by the ag-grid on the same. Here is the reference link
import React, { useState, useEffect, useMemo, useRef } from "react";
import { AgGridReact } from "ag-grid-react";
import Tippy from "#tippyjs/react";
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";
function ActionsMenu(props) {
const tippyRef = useRef();
const [visible, setVisible] = useState(false);
const show = () => setVisible(true);
const hide = () => setVisible(false);
const menu = (
<div className="menu-container">
<div className="menu-item" onClick={hide}>
Create
</div>
<div className="menu-item" onClick={hide}>
Edit
</div>
<div className="menu-item" onClick={hide}>
Delete
</div>
</div>
);
return (
<Tippy
ref={tippyRef}
content={menu}
visible={visible}
onClickOutside={hide}
allowHTML={true}
arrow={false}
appendTo={document.body}
interactive={true}
placement="right"
// moveTransition='transform 0.1s ease-out'
>
<button onClick={visible ? hide : show}>Actions</button>
</Tippy>
);
}
const frameworkComponents = {
ActionsMenu: ActionsMenu,
};
export default function App() {
const [rowData, setRowData] = useState([
{ make: "Ford", model: "Focus", price: 20000 },
{ make: "Toyota", model: "Celica", price: 40000 },
{ make: "BMW", model: "4 Series", price: 50000 },
]);
const [columnDefs, setColumnDefs] = useState([
{ field: "make" },
{ field: "model" },
{ field: "price" },
{ field: "", cellRenderer: "ActionsMenu" },
]);
const defaultColDef = useMemo(
() => ({
sortable: true,
filter: true,
}),
[]
);
useEffect(() => {
fetch("https://www.ag-grid.com/example-assets/row-data.json")
.then((result) => result.json())
.then((r) => setRowData(r));
}, []);
return (
<div className="ag-theme-alpine" style={{ height: 500, width: "100%" }}>
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
frameworkComponents={frameworkComponents}
/>
</div>
);
}
I am working on React Table. I am basically a beginner in React. I have a dashboard page where I display a React Table of 8 columns. I have a customize button which will open a popup page, this popup page has 8 check boxes allows me to show/hide those React columns. Initially all the check boxes in this popup page is set to true. When I uncheck a column that particular column get disabled.
There are images in the end to see what I am trying to do.
I will be using this logic for show hide columns (this question was asked by me two days back) - You can see how this is done using a React Table attribute/property called show. When show is true that column/sub column is shown and When show is false that column/sub column is hidden.
The React Table data is like this
const columns = [
{
Header: 'Column 1',
accessor: 'firstName',
// show: true // shows the particular column (true is default)
// show: false // hides the particular column
},
{
Header: 'Column 2',
accessor: 'firstName',
},
{
Header: 'Column 3',
accessor: 'firstName',
},
{
Header: 'Column 4',
accessor: 'firstName',
},
{
Header: 'Column 5',
accessor: 'firstName',
},
{
Header: 'Column 6',
accessor: 'firstName',
},
{
Header: 'Column 7',
accessor: 'firstName'
},
{
Header: 'Column 8',
accessor: 'firstName',
}
];
Now have a look at this image. The image shows my dashboard page along with the checkbox popup page which can turn my column on/off (or show/hide)
This is the code for the popup page for checkbox
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { ActionCreators } from '../../../actions';
import ButtonComponent from '../../common/button/ButtonComponent';
import { CheckBox } from '../../common/chkbox/CheckBox';
class CustomizedView extends Component {
constructor(props) {
super(props);
this.handleCheckChildElement = this.handleCheckChildElement.bind(this);
this.state = {
items: [
{ id: 1, value: 'Column 1', isChecked: true },
{ id: 2, value: 'Column 2', isChecked: true },
{ id: 3, value: 'Column 3', isChecked: true },
{ id: 4, value: 'Column 4', isChecked: true },
{ id: 5, value: 'Column 5', isChecked: true },
{ id: 6, value: 'Column 6', isChecked: true },
{ id: 7, value: 'Column 7', isChecked: true },
{ id: 8, value: 'Column 8', isChecked: true },
]
};
}
handleClick() {
this.setState({ isChecked: !this.state.isChecked });
}
handleCheckChildElement(event) {
const { items } = this.state; //extract state values like this to a const variable
const newItems = items.map((item) => { //do map on items because map returns a new array. It’s good practice to use .map than forEach in your case
if(item.value === event.target.value) {
item.isChecked = event.target.checked;
return item; //return updated item object so that it will be pushed to the newItems array
}
return item; // return item because you need this item object as well
});
this.setState({ items: newItems }); //finally set newItems array into items
const column1checked = items[0].isChecked;
console.log('column1checked ' + column1checked);
const column2checked = items[1].isChecked;
console.log('column2checked ' + column2checked);
const column3checked = items[2].isChecked;
console.log('column3checked ' + column3checked);
const column4checked = items[3].isChecked;
console.log('column4checked ' + column4checked);
const column5checked = items[4].isChecked;
console.log('column5checked ' + column5checked);
const column6checked = items[5].isChecked;
console.log('column6checked ' + column6checked);
const column7checked = items[6].isChecked;
console.log('column7checked ' + column7checked);
const column8checked = items[7].isChecked;
console.log('column8checked ' + column8checked);
}
render() {
return (
<div className='div-container-custom' >
<div className='bottomBar'>
<ButtonComponent
text='Apply'
className='activeButton filterMargin-custom'
width='100'
display='inline-block'
onClick={() => { this.props.applyFilter(this.state, false); }}
/>
<ButtonComponent
text='Clear Filter'
className='greyedButton clear-custom-filter'
width='100'
display='block'
marginTop='60'
onClick={() => { this.props.applyFilter(this.state, true); }}
/>
</div>
<div>
<div className='data-points-text'>
<span> Columns </span>
</div>
<div className="App">
<ul>
{
this.state.items.map((item, i) => {
return (<div key={i} ><CheckBox handleCheckChildElement={this.handleCheckChildElement} {...item} /></div>);
})
};
</ul>
</div>
</div>
</div>
);
}
}
CustomizedView.propTypes = {
applyFilter: PropTypes.func.isRequired
};
CustomizedView.defaultProps = {
};
function mapStateToProps(state) {
return {
auth: state.auth
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(ActionCreators, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(CustomizedView);
Now my point is when I only uncheck 4th column in the checkbox popup page I get the desired values (the above code)
column1checked true
column2checked true
column3checked true
column4checked false
column5checked true
column6checked true
column7checked true
column8checked true
And ultimately this is my checkbox page
import React from 'react';
import PropTypes from 'prop-types';
export const CheckBox = (props) => {
// super(props);
return (
<li>
<input key={props.id} onClick={props.handleCheckChildElement} type="checkbox" checked={props.isChecked} value={props.value} /> {props.value}
</li>
);
};
CheckBox.propTypes = {
id: PropTypes.string,
handleCheckChildElement: PropTypes.func,
isChecked: PropTypes.bool,
value: PropTypes.string,
};
export default CheckBox;
Now my question is I have the constants in CustomizedView Component. I need those const variables available in ReactTable Component (path is like this - '../../common/chkbox/CheckBox')
In the main dashboard page I import the React Table
import ReactTableComponent from '../common/react_table/ReactTableComponent';
and just use it in the render function of the main dashboard page in this way.
<ReactTableComponent />
So I need to use all those const variables of Customized variables in ReactTable Component to show/hide tables.
Also here I have to use show: some_const (I cannot use true or false)
I am a complete beginner in React and I need help from this community. Kindly help to implement this idea.
All I want to know the best/easiest (may be great way or dumb way) way to transfer those 8 const variables from components\abcdashboard\customized_view\Customizedview to components\common\react_table\ReactTableComponent
Generally, when you want to share data across components like this, what you want is to consolidate the data in a higher-up component, and use functions to modify it.
Here's a contrived example (CodeSandbox here):
import React from "react";
import ReactDOM from "react-dom";
class Parent extends React.Component {
state = {
name: "Bill"
};
changeName = name => {
this.setState({ name });
};
render() {
return (
<React.Fragment>
<h1>name is: {this.state.name}</h1>
<ChildA changeName={this.changeName} />
<ChildB changeName={this.changeName} />
</React.Fragment>
);
}
}
class ChildA extends React.Component {
state = {
nameA: "Steve"
};
render() {
return (
<button onClick={() => this.props.changeName(this.state.nameA)}>
change to my name
</button>
);
}
}
class ChildB extends React.Component {
state = {
nameB: "Elon"
};
render() {
return (
<button onClick={() => this.props.changeName(this.state.nameB)}>
change to my name
</button>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Parent />, rootElement);
Here, the Parent can access the value of the names of the children through the changeName function which is passed down.
If you just want to share variables and not data per se, you could just define a whole file and export various variables like:
export const PI = 3.14
and so on, then do like:
import { PI } from './variables'
in other files, for example.
I've been trying to make a locker organization / assignment app for a school project, and I've seen to hit a roadblock at the displaying of the firebase database. I found React-Table, which seemed like a good way to display the data, but I've had some issues actually displaying the data. When I run the app, it says that no rows are found.
Below is the code for my LockTable.js component.
import React, { Component } from 'react';
import firebase from 'firebase/app';
import { auth } from '../firebase';
import 'firebase/database';
//import { database } from '../firebase';
import ReactTable from "react-table";
import 'react-table/react-table.css';
class LockTable extends Component{
constructor(props){
super(props);
}
render(){
const database = firebase.database().ref("LockerList");
const data1 = [];
database.on('value',snapshot => {
//Read each item in LockerList
//Store it in a temporary array
snapshot.forEach(childSnapShot => {
//childSnapShot.key is the name of the data
//childSnapShot.val() is the value of the data
const Locker = {
LockerNumber : childSnapShot.key.toString(),
Available : childSnapShot.val().Available,
StudentName : childSnapShot.val().StudentName,
StudentNumber : childSnapShot.val().StudentNumber.toString() ,
Period : childSnapShot.val().StudentPeriod.toString(),
Teacher : childSnapShot.val().Teacher,
};
data1.push(childSnapShot.val());
});
});
const columns = [
{
Header: 'Locker Number',
accessor: 'LockerNumber'
}, {
Header: 'Available',
accessor: 'Available',
}, {
Header: 'Student Name',
accessor: 'StudentName',
}, {
Header: 'Student Number',
accessor: 'StudentNumber',
}, {
Header: 'Period',
accessor: 'Period',
}, {
Header: 'Teacher',
accessor: 'Teacher',
} ];
console.log(data1);
return(
<div>
<ReactTable
data={data1}
columns={columns}
/>
</div>
);
}
}
export default LockTable;
This is what my firebase database looks like
This is what the data I read from firebase looks like
To test React-Table out, I made a StackBlitz with handmade variables rather than variables read from firebase.
https://stackblitz.com/edit/react-table-help
If you open the console, you can see that the data from the StackBlitz version is the exact same as the data from my version, yet, the StackBlitz version is displaying the data, while my version is not.
Does anyone know why my version doesn't work? Any help would be appreciated!
The render method can only handle synchronous logic. The firebase database logic is asynchronous, so to use it you can put it in componentDidMount instead and put the data in the component state when you get a snapshot.
Example
class LockTable extends Component {
state = { data: [] };
componentDidMount() {
const database = firebase.database().ref("LockerList");
database.on("value", snapshot => {
const data = [];
snapshot.forEach(childSnapShot => {
const locker = {
LockerNumber: childSnapShot.key.toString(),
Available: childSnapShot.val().Available,
StudentName: childSnapShot.val().StudentName,
StudentNumber: childSnapShot.val().StudentNumber.toString(),
Period: childSnapShot.val().StudentPeriod.toString(),
Teacher: childSnapShot.val().Teacher
};
data.push(locker);
});
this.setState(prevState => {
return { data: [...prevState.data, ...data] };
});
});
}
render() {
const columns = [
{
Header: "Locker Number",
accessor: "LockerNumber"
},
{
Header: "Available",
accessor: "Available"
},
{
Header: "Student Name",
accessor: "StudentName"
},
{
Header: "Student Number",
accessor: "StudentNumber"
},
{
Header: "Period",
accessor: "Period"
},
{
Header: "Teacher",
accessor: "Teacher"
}
];
return (
<div>
<ReactTable data={this.state.data} columns={columns} />
</div>
);
}
}