Open bootstrap modal after axios http call in react - reactjs

I made an HTTP call in react using Axios. It works perfectly fine. But when I try to open a bootstrap 4 modal after HTTP call success. It shows me an error 'modal is not a function'. I try a number of ways to solve this but unable to solve the problem. I didn't upload the whole code as it is quite long. Let me know in the comments if you want any additional code sample. Please help.
import $ from 'jquery';
import '../assets/css/signup.css';
import { Link } from 'react-router-dom';
import axios from 'axios';
import SuccessMessage from './dashboard/SuccessMessage';
class SignUp extends React.Component{
constructor()
{
super()
this.state={
firstName:'',
lastName:'',
email:'',
phoneNumber:'',
password:'',
confirmPassword:'',
isSignUp:false
}
}
componentDidUpdate()
{
if(this.state.isSignUp === true)
{
let user = {
firstName: this.state.firstName,
lastName: this.state.lastName,
email:this.state.email,
phoneNumber:this.state.phoneNumber,
password:this.state.password
}
console.log(user);
var first_name = user.firstName;
var last_name=user.lastName;
var email=user.email;
var phone_no=user.phoneNumber;
var password = user.password;
axios.post("http://ec2-14-2a9-69-0b6.us-west-2.compute.amazonaws.com:4000/dashboard/register", {
first_name,
last_name,
email,
phone_no,
password
}, {
headers: header
})
.then(res => {
console.log(res);
if(res.status === 200 && res.data.success === true)
{
setTimeout(() =>
{
$('#signup-success').modal('show');
},200)
}
})
}
}
handleSubmit=(e) =>
{
e.preventDefault();
this.setState({isSignUp:true});
}
render()
{
return(
<SuccessMessage heading="Sign Up Successfully!" description="Please login in to access your account" iconClass="fa fa-check bg-golden flex all-center border-radius-50" modalId="signup-success"/>
)
}
Success Message component
<div className="modal" id={this.props.modalId}>
<div className="modal-dialog modal-dialog-centered">
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">Modal Heading</h4>
<button type="button" className="close" data-dismiss="modal">×</button>
</div>
<div className="modal-body align-center" style={style}>
<i style={icon} className={this.props.iconClass} ></i>
<h3 className="heading color-black">{this.props.heading}</h3>
<p className="paragraph color-black">{this.props.description}</p>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

Try not to use jquery and react together. You could achieve what you are saying using the react state:
class SignUp extends React.Component{
constructor()
{
super()
this.state={
firstName:'',
lastName:'',
email:'',
phoneNumber:'',
password:'',
confirmPassword:'',
isSignUp:false,
showModal: false
}
}
componentDidUpdate()
{
if(this.state.isSignUp === true)
{
let user = {
firstName: this.state.firstName,
lastName: this.state.lastName,
email:this.state.email,
phoneNumber:this.state.phoneNumber,
password:this.state.password
}
console.log(user);
var first_name = user.firstName;
var last_name=user.lastName;
var email=user.email;
var phone_no=user.phoneNumber;
var password = user.password;
axios.post("http://ec2-14-2a9-69-0b6.us-west-2.compute.amazonaws.com:4000/dashboard/register", {
first_name,
last_name,
email,
phone_no,
password
}, {
headers: header
})
.then(res => {
console.log(res);
if(res.status === 200 && res.data.success === true)
{
setTimeout(() =>
{
this.setState({ showModal: true });
},200)
}
})
}
}
handleSubmit=(e) =>
{
e.preventDefault();
this.setState({isSignUp:true});
}
render()
{
return(
<div>
{
this.state.showModal &&
<SuccessMessage heading="Sign Up Successfully!" description="Please login in to access your account" iconClass="fa fa-check bg-golden flex all-center border-radius-50" modalId="signup-success"/>
</div>
)
}
Also, I guess you got a display: none or something in the modal as you are doing a .show using jquery. Put that to display always as it will be only shown if the state is true.

Actually getting the Bootstrap Modal to display using React (without jQuery) requires DOM manipulation. Bootstrap 4 uses jQuery to add a modal backdrop element, adds the modal-open class to the body, and finally adds display:block to the .modal wrapper.
This is why it's preferable to using reactstrap, react-bootstrap, etc... since they've already componentized the Bootstrap Modal.
If you must show (toggle) the Bootstrap Modal in React without jQuery (or other component framework), here's an example:
class SuccessMessage extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
modalClasses: ['modal','fade']
}
}
toggle() {
document.body.className += ' modal-open'
let modalClasses = this.state.modalClasses
if (modalClasses.indexOf('show') > -1) {
modalClasses.pop()
//hide backdrop
let backdrop = document.querySelector('.modal-backdrop')
document.body.removeChild(backdrop)
}
else {
modalClasses.push('show')
//show backdrop
let backdrop = document.createElement('div')
backdrop.classList = "modal-backdrop fade show"
document.body.appendChild(backdrop)
}
this.setState({
modalClasses
})
}
render() {
return (
<div
id="messageModal"
className={this.state.modalClasses.join(' ')}
tabIndex="-1"
role="dialog"
aria-hidden="true"
ref="messageModal"
>
<div className="modal-dialog modal-dialog-centered modal-lg">
<div className="modal-content">
<div className="modal-header">
<h4>
Success
</h4>
...
</div>
<div className="modal-body">
...
</div>
</div>
</div>
</div>
)
}
}
Working Demo: https://codeply.com/p/4EV36QjwCB

Related

How do I call an event for another component in React?

I've been working in react for about.. 2 days. 3/4's of that was me trying to get react to work with my current Asp.net MVC WebApp.
I have 2 classes. Everything is working fine, but I want the handleScan of Camera to call the handleHideClick() of ShowScanner. Whats the proper way to do that?
Here is my code:
import React from 'react';
import QrReader from 'react-qr-reader';
import { unmountComponentAtNode, render } from "react-dom";
class Camera extends React.Component {
state = {
result: 'No result'
}
handleScan = data => {
if (data) {
this.setState({
result: data
});
scanDataProcessor(this.state.result);
unmountComponentAtNode(document.getElementById('root'));
// *** Replace unmount above with a call to execute: ShowScanner.handleHideClick()
}
}
handleError = err => {
console.error(err)
}
render() {
return (
<div id="scanner" class="">
<QrReader
delay={300}
onError={this.handleError}
onScan={this.handleScan}
style={{ width: '100%' }}
/>
</div>
)
}
}
class ShowScanner extends React.Component {
constructor(props) {
super(props);
this.handleShowClick = this.handleShowClick.bind(this);
this.handleHideClick = this.handleHideClick.bind(this);
this.state = { showCamera: false }
}
handleShowClick() {
this.setState({ showCamera: true });
render(<Camera />, document.getElementById('root'));
}
handleHideClick() {
this.setState({ showCamera: false });
unmountComponentAtNode(document.getElementById('root'));
}
render() {
const showCamera = this.state.showCamera;
let button;
if (showCamera) {
return (
<button id="btnScan" type="button" class="btn btn-primary p-1" onClick={this.handleHideClick}>
<i class="bi bi-qr-code-scan"></i>
<br />Scan
</button>
);
} else {
return (
<button id="btnScan" type="button" class="btn btn-primary p-1" onClick={this.handleShowClick}>
<i class="bi bi-qr-code-scan"></i>
<br />Scan
</button>
);
}
}
}
render(<ShowScanner />, document.getElementById('scanBtn'));
At the very least, I need to reset the button so that when they click it, it remounts the Camera component without them having to click it twice.
Thanks in advance!

How to make create/edit popup form component

I'm trying to make simple CRUD example using react.js as frontend.
I already have add/edit functionality done in a component,
but I want to call this component dynamically on click and show it as a popup or modal window on the same page without redirecting to another route.
Does anyone have experience with doing this using react.js?
This is my parent component code where I show a grid of items displaying cities:
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { Link, NavLink } from 'react-router-dom';
interface FetchNaseljeDataState {
nasList: NaseljeData[];
loading: boolean;
}
export class FetchNaselje extends React.Component<RouteComponentProps<{}>, FetchNaseljeDataState> {
constructor() {
super();
this.state = { nasList: [], loading: true };
fetch('api/Naselje/Index')
.then(response => response.json() as Promise<NaseljeData[]>)
.then(data => {
this.setState({ nasList: data, loading: false });
});
// This binding is necessary to make "this" work in the callback
this.handleDelete = this.handleDelete.bind(this);
this.handleEdit = this.handleEdit.bind(this);
}
public render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: this.renderNaseljeTable(this.state.nasList);
return <div>
<h1>Naselje Data</h1>
<p>This component demonstrates fetching Naselje data from the server.</p>
<p>
<Link to="/addnaselje">Create New</Link>
</p>
{contents}
</div>;
}
// Handle Delete request for an naselje
private handleDelete(id: number) {
if (!confirm("Do you want to delete naselje with Id: " + id))
return;
else {
fetch('api/Naselje/Delete/' + id, {
method: 'delete'
}).then(data => {
this.setState(
{
nasList: this.state.nasList.filter((rec) => {
return (rec.idnaselje != id);
})
});
});
}
}
private handleEdit(id: number) {
this.props.history.push("/naselje/edit/" + id);
}
// Returns the HTML table to the render() method.
private renderNaseljeTable(naseljeList: NaseljeData[]) {
return <table className='table'>
<thead>
<tr>
<th></th>
<th>ID Naselje</th>
<th>Naziv</th>
<th>Postanski Broj</th>
<th>Drzava</th>
</tr>
</thead>
<tbody>
{naseljeList.map(nas =>
<tr key={nas.idnaselje}>
<td></td>
<td>{nas.idnaselje}</td>
<td>{nas.naziv}</td>
<td>{nas.postanskiBroj}</td>
<td>{nas.drzava && nas.drzava.naziv}</td>
<td>
<a className="action" onClick={(id) => this.handleEdit(nas.idnaselje)}>Edit</a> |
<a className="action" onClick={(id) => this.handleDelete(nas.idnaselje)}>Delete</a>
</td>
</tr>
)}
</tbody>
</table>;
}
}
export class NaseljeData {
idnaselje: number = 0;
naziv: string = "";
postanskiBroj: string = "";
drzava: DrzavaData = { iddrzava: 0, naziv: ""};
drzavaid: number = 0;
}
export class DrzavaData {
iddrzava: number = 0;
naziv: string = "";
}
This is my child component that I want to dynamically show on create new link click:
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { Link, NavLink } from 'react-router-dom';
import { NaseljeData } from './FetchNaselje';
import { DrzavaData } from './FetchNaselje';
interface AddNaseljeDataState {
title: string;
loading: boolean;
drzavaList: Array<any>;
nasData: NaseljeData;
drzavaId: number;
}
export class AddNaselje extends React.Component<RouteComponentProps<{}>, AddNaseljeDataState> {
constructor(props) {
super(props);
this.state = { title: "", loading: true, drzavaList: [], nasData: new NaseljeData, drzavaId: -1 };
fetch('api/Naselje/GetDrzavaList')
.then(response => response.json() as Promise<Array<any>>)
.then(data => {
this.setState({ drzavaList: data });
});
var nasid = this.props.match.params["nasid"];
// This will set state for Edit naselje
if (nasid > 0) {
fetch('api/Naselje/Details/' + nasid)
.then(response => response.json() as Promise<NaseljeData>)
.then(data => {
this.setState({ title: "Edit", loading: false, nasData: data });
});
}
// This will set state for Add naselje
else {
this.state = { title: "Create", loading: false, drzavaList: [], nasData: new NaseljeData, drzavaId: -1 };
}
// This binding is necessary to make "this" work in the callback
this.handleSave = this.handleSave.bind(this);
this.handleCancel = this.handleCancel.bind(this);
}
public render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: this.renderCreateForm(this.state.drzavaList);
return <div>
<h1>{this.state.title}</h1>
<h3>Naselje</h3>
<hr />
{contents}
</div>;
}
// This will handle the submit form event.
private handleSave(event) {
event.preventDefault();
const data = new FormData(event.target);
// PUT request for Edit naselje.
if (this.state.nasData.idnaselje) {
fetch('api/Naselje/Edit', {
method: 'PUT',
body: data,
}).then((response) => response.json())
.then((responseJson) => {
this.props.history.push("/fetchnaselje");
})
}
// POST request for Add naselje.
else {
fetch('api/Naselje/Create', {
method: 'POST',
body: data,
}).then((response) => response.json())
.then((responseJson) => {
this.props.history.push("/fetchnaselje");
})
}
}
// This will handle Cancel button click event.
private handleCancel(e) {
e.preventDefault();
this.props.history.push("/fetchnaselje");
}
// Returns the HTML Form to the render() method.
private renderCreateForm(drzavaList: Array<any>) {
return (
<form onSubmit={this.handleSave} >
<div className="form-group row" >
<input type="hidden" name="idnaselje" value={this.state.nasData.idnaselje} />
</div>
< div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Naziv">Naziv</label>
<div className="col-md-4">
<input className="form-control" type="text" name="naziv" defaultValue={this.state.nasData.naziv} required />
</div>
</div >
<div className="form-group row">
<label className="control-label col-md-12" htmlFor="PostanskiBroj" >Postanski broj</label>
<div className="col-md-4">
<input className="form-control" name="PostanskiBroj" defaultValue={this.state.nasData.postanskiBroj} required />
</div>
</div>
<div className="form-group row">
<label className="control-label col-md-12" htmlFor="Drzava">Država</label>
<div className="col-md-4">
<select className="form-control" data-val="true" name="drzavaid" defaultValue={this.state.nasData.drzava ? this.state.nasData.drzava.naziv : ""} required>
<option value="">-- Odaberite Državu --</option>
{drzavaList.map(drzava =>
<option key={drzava.iddrzava} value={drzava.iddrzava}>{drzava.naziv}</option>
)}
</select>
</div>
</div >
<div className="form-group">
<button type="submit" className="btn btn-default">Save</button>
<button className="btn" onClick={this.handleCancel}>Cancel</button>
</div >
</form >
)
}
}
I'm assuming I'll have to make css for the create/edit component to make it look like a popup...
EDIT: I would appreciate if someone could make code example using my classes, thanks...
In the parent component set a state on click functionality, say for eg:
this.setState({display: true})
In the parent component render based on condition display child component, say for eg:
<div>{(this.state.display) ? <div><childComponent /></div> : ''}</div>
To display the child component in a modal/popup, put the component inside say a bootstrap or react-responsive-modal. For that, you have to install and import react-responsive-modal and then
In the render method,
return (
<div>
{this.state.toggleModal ? <div className="container">
<Modal open={this.state.toggleModal} onClose={this.onCloseModal} center>
<div className="header">
<h4>{Title}</h4>
</div>
<div className="body">
<div>
{this.state.toggleModal ? <someComponent /> : ''}
</div>
</div>
</Modal>
</div>
: null}
</div>
)
Have your popup component receive a prop from the parent that will tell it if it should be displayed or not, a simple boolean will do the trick. Then, when you want something to show the popup, just change that state in the parent.

Bootstrap v4 popover not updating on state change in React

I have a "Nightlife Coordination" app (from the Free Code Camp curriculum) that allows a user to search by city and RSVP to a bar for that night. The app keeps a list of who has RSVP'd and who is going. It is built with React and Bootstrap v4 (and Node on the back end).
I have text under each bar location that, when clicked, allows a user to RSVP or unRSVP. There is also a button that shows how many people have RSVP'd and, if clicked, will display a Bootstrap popover of the list of people who have RSVP'd.
If a user RSVPs (or unRSVPs), I want the list to update. (Currently, the number on the button DOES update, but not the list.)
The following two images show the problem:
Upon initial load, all is correctly functional
When the user RSVPS or unRSVPs, the number on the button correctly updates, but the list does not
Here is my code.
The list is being generated in the data-content attribute in the second anchor tag in the render method.
Can anyone help?
One other hint is that in my React developer tools Chrome extension, it shows the data-content attribute correctly updating upon RSVP and unRSVP. Is it that perhaps Bootstrap saves the contents of the data-content attribute in its JS file upon initial render and does not update it?
const React = require('react');
class Bar extends React.Component {
constructor(props) {
super(props);
this.state = {
countMeIn: false, // coming from Mongo
numberGoing: this.props.user_namesArr.length,
user_id: this.props.twitter_id,
user_name: this.props.user_name,
yelp_id: this.props.yelp_id,
user_namesArr: this.props.user_namesArr
};
}
componentDidMount() { // need the same for DidMount and DidUpdate, in case user is signed in upon load (from previous session), or signs in after load
if (this.state.user_namesArr.includes(this.props.user_name) && !this.state.countMeIn) {
this.setState({
countMeIn: true
});
}
}
componentDidUpdate(prevProps, prevState) { // Need both in case user logs in after initial page load
console.log(this.state.user_namesArr);
if (this.state.user_namesArr.includes(this.props.user_name) && !prevState.countMeIn) {
this.setState({
countMeIn: true
});
}
$('[data-toggle="popover"]').popover();
}
rsvp() {
let url = '/rsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
fetch(url, { method: "POST" })
.then((res) => res.json())
.then((json) => {
let newArr = this.state.user_namesArr;
newArr.push(this.props.user_name);
this.setState({
numberGoing: this.state.numberGoing + 1,
countMeIn: true,
user_namesArr: newArr,
});
})
}
unrsvp() {
let url = '/unrsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
fetch(url, { method: "POST" })
.then((res) => res.json())
.then((json) => {
let ind = this.state.user_namesArr.indexOf(this.props.user_name);
let newArr = this.state.user_namesArr;
newArr.splice(ind, 1);
this.setState({
numberGoing: this.state.numberGoing - 1,
countMeIn: false,
user_namesArr: newArr,
});
})
}
render() {
return (
<div className="col-lg-4 onecomponent">
<a href={ this.props.bar_yelp_url } target="_blank">
<div className="barname text-center">
{ this.props.name }
</div>
<div className="priceline">
<img className="stars" src={ this.state.starsUrl } /> { this.props.review_count } reviews <span className="price">{ this.props.price }</span>
</div>
<div className="image">
<img class="mainimg" src={ this.props.image_url } />
</div>
<div className="address text-center">
{ this.props.loc[0] }., { this.props.loc[1] }
</div>
</a>
<hr/>
<div className="text-center">
<a tabindex="0" role="button" className="btn btn-success" data-toggle={ this.state.user_namesArr.length > 0 ? "popover" : "" } data-trigger="focus" title="Who's In?" data-content={ this.state.user_namesArr }>
{ this.state.numberGoing } going
</a>
{
this.props.loggedIn ?
this.state.countMeIn ?
<span className="going" onClick={ () => this.unrsvp() }>You're going!</span> : // if logged in and already RSVP'd
<span className="rsvpdetails" onClick={ () => this.rsvp() }>Count me in!</span> : // if logged in but not yet RSVP'd
<span> Please log in </span> // if not logged in
}
</div>
</div>
)
}
}
module.exports = Bar;
Maybe using ref could help ... but why not use reactstrap and more important why not react-popper ...? It's well known (https://github.com/FezVrasta/popper.js/#react-vuejs-angular-angularjs-emberjs-etc-integration) that many libraries doesn't work well with react or any other (virtual) DOM managers.
Do you really need jQuery?
Using react portals you can remove all theese dependencies.
It works with Reactstrap. I simply added reactstrap to my package.json file, and used the Reactstrap code.
const React = require('react');
import { Button, Popover, PopoverHeader, PopoverBody } from 'reactstrap';
class Bar extends React.Component {
constructor(props) {
super(props);
this.state = {
countMeIn: false, // coming from Mongo
numberGoing: this.props.user_namesArr.length,
user_id: this.props.twitter_id,
user_name: this.props.user_name,
yelp_id: this.props.yelp_id,
user_namesArr: this.props.user_namesArr,
popover: false
};
this.toggle = this.toggle.bind(this);
}
componentDidMount() { // need the same for DidMount and DidUpdate, in case user is signed in upon load (from previous session), or signs in after load
if (this.state.user_namesArr.includes(this.props.user_name) && !this.state.countMeIn) {
this.setState({
countMeIn: true
});
}
}
componentDidUpdate(prevProps, prevState) {
if (this.state.user_namesArr.includes(this.props.user_name) && !prevState.countMeIn) {
this.setState({
countMeIn: true
});
}
}
rsvp() {
let url = '/rsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
fetch(url, { method: "POST" })
.then((res) => res.json())
.then((json) => {
let newArr = this.state.user_namesArr;
newArr.push(this.props.user_name);
this.setState({
user_namesArr: newArr,
numberGoing: this.state.numberGoing + 1,
countMeIn: true
});
})
}
unrsvp() {
let url = '/unrsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
fetch(url, { method: "POST" })
.then((res) => res.json())
.then((json) => {
let ind = this.state.user_namesArr.indexOf(this.props.user_name);
let newArr = this.state.user_namesArr;
newArr.splice(ind, 1);
this.setState({
user_namesArr: newArr,
numberGoing: this.state.numberGoing - 1,
countMeIn: false
});
})
}
toggle() {
this.setState({
popover: !this.state.popover
});
}
render() {
return (
<div className="col-lg-4 onecomponent">
<a href={ this.props.bar_yelp_url } target="_blank">
<div className="barname text-center">
{ this.props.name }
</div>
<div className="priceline">
<img className="stars" src={ this.state.starsUrl } /> { this.props.review_count } reviews <span className="price">{ this.props.price }</span>
</div>
<div className="image">
<img class="mainimg" src={ this.props.image_url } />
</div>
<div className="address text-center">
{ this.props.loc[0] }., { this.props.loc[1] }
</div>
</a>
<hr/>
<div className="text-center">
{ /* For this to work, id must have leading letters, otherwise throws massive errors. See here: https://stackoverflow.com/questions/23898873/failed-to-execute-queryselectorall-on-document-how-to-fix */ }
<Button id={ "abc" + this.props.yelp_id } className="btn btn-success" onClick={ this.toggle }>{ this.state.numberGoing } going</Button>
<Popover placement="right" isOpen={ this.state.popover } target={ "abc" + this.props.yelp_id } toggle={ this.toggle }>
<PopoverHeader>Who's In?</PopoverHeader>
<PopoverBody>{ this.state.user_namesArr }</PopoverBody>
</Popover>
{
this.props.loggedIn ?
this.state.countMeIn ?
<span className="going" onClick={ () => this.unrsvp() }>You're going!</span> : // if logged in and already RSVP'd
<span className="rsvpdetails" onClick={ () => this.rsvp() }>Count me in!</span> : // if logged in but not yet RSVP'd
<span> Please log in </span> // if not logged in
}
</div>
</div>
)
}
}
module.exports = Bar;

Reactjs use function from parent

I have 2 files. One is my app.js and the other one is productmodal.js
app.js gets a productlist from an api call. It views these productst in a list.
When a user clicks on the image productmodal.js is showed.
productmodal.js shows a popup with a bigger image, productname(title) and a button called "Product niet aanwezig" (product unavailable) if the user clicks on this link a mail is send to an other user.
When the button is click I also want to activate an other function (getUpdatedProduct). This function is gonna do a long polling call to a link to check when the product is updated to a new product and update this product in the app.
The problem is: I don't know how to call the function 'getUpdatedProduct' in productmodal.js
I get an error: Uncaught TypeError: Cannot read property 'getUpdatedProduct' of undefined
I tried some of these solutions https://reactjs.org/docs/faq-functions.html. Especially the arrow function in render, because its generating a new function every time when the modal is clicked (which I need).
But nothing seems to work. Has anyone some idea's?
App.js:
import React from 'react';
import image from '../images/sogyologo.svg';
import ProductModal from './ProductModal.js';
class App extends React.Component {
constructor(props) {
super(props);
this.toggleModal = this.toggleModal.bind(this);
this.state = {
isLoading: true,
orders: [],
dealtOrders: [],
productDetail: [],
open: false,
modal: []
}
}
toggleModal(event)
{
console.log(event);
let itemIndex = event.target.getAttribute("data-itemIndex");
console.log(itemIndex);
const productModal = this.state.orders[itemIndex];
console.log(productModal);
this.setState({
open: true,
modal: this.state.orders[itemIndex]
});
}
handleClose() {
this.setState({
open: !this.state.open
});
}
componentWillMount() {
localStorage.getItem('orders') && this.setState({
orders: JSON.parse(localStorage.getItem('orders')),
isLoading: false
})
}
componentDidMount() {
if (!localStorage.getItem('orders')){
this.fetchData();
} else {
console.log('Using data from localstorage');
}
}
fetchData() {
fetch('http://localhost:54408/api/orders/all/testing-9!8-7!6/10-04-2018')
.then(response => response.json())
.then(parsedJSON => parsedJSON.map(product => (
{
productname: `${product.ProductName}`,
image: `${product.Image}`,
quantity: `${product.Quantity}`,
isconfirmed: `${product.IsConfirmed}`,
orderid: `${product.OrderId}`
}
)))
.then(orders => this.setState({
orders,
isLoading: false
}))
.catch(error => console.log('parsing failed', error))
}
// componentWillUpdate(nextProps, nextState) {
// localStorage.setItem('orders', JSON.stringify(nextState.orders));
// localStorage.setItem('ordersDate', Date.now());
// }
render() {
this.handleDoneAction = event =>
{
let itemIndex = event.target.getAttribute("data-itemIndex");
let prevOrders = [...this.state.orders];
let dealtOrders = [...this.state.dealtOrders];
const itemToMoveAtLast = prevOrders.splice(itemIndex, 1);
const addToDealtOrders = dealtOrders.concat(itemToMoveAtLast);
this.setState({dealtOrders: addToDealtOrders});
this.setState({orders: prevOrders});
};
this.handleUndoAction = event =>
{
let itemIndex = event.target.getAttribute("data-itemIndex");
let orders = [...this.state.orders];
let dealtOrders = [...this.state.dealtOrders];
const undoDealtOrder = dealtOrders.splice(itemIndex, 1);
const addToOrders = orders.concat(undoDealtOrder);
this.setState({orders: addToOrders});
this.setState({dealtOrders: dealtOrders});
};
const {isLoading, orders, dealtOrders} = this.state;
return (
<div>
<header>
<img src={image}/>
<h1>Boodschappenlijstje <button className="btn btn-sm btn-danger">Reload</button></h1>
</header>
<div className={`content ${isLoading ? 'is-loading' : ''}`}>
<div className="panel">
{
!isLoading && orders.length > 0 ? orders.map((order, index) => {
const {productname, image, quantity, orderid} = order;
return<div className="product" key={orderid}>
<div className="plaatjediv" onClick={this.toggleModal.bind(this) }>
<img className="img-responsive" data-itemIndex={index} src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
</div>
<div className="bdone">
<button className="btn btn-lg btn-default btndone" data-itemIndex={index} onClick={this.handleDoneAction}>Done</button>
</div>
</div>
}) : null
}
</div>
<h2>Mandje</h2>
<div className="panel">
{
!isLoading && dealtOrders.length > 0 ? dealtOrders.map((dorder, index) => {
const {productname, image, quantity} = dorder;
return<div className="productDone" key={index}>
<div className="plaatjediv">
<img className="img-responsive" src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
</div>
<div className="bdone">
<button className="btn btn-lg btn-default btndone" data-itemIndex={index} onClick={this.handleUndoAction}>Undo</button>
</div>
</div>
}) : null
}
</div>
<ProductModal open={this.state.open} handleClose={this.handleClose.bind(this)}
title={this.state.modal.productname} plaatje={this.state.modal.image} orderid={this.state.modal.orderid}/>
<div className="loader">
<div className="icon"></div>
</div>
</div>
</div>
);
}
}
export default App;
productmodal.js
import React from 'react';
class ProductModal extends React.Component {
constructor() {
super();
this.getUpdatedProduct = this.getUpdatedProduct.bind(this);
}
handleClose() {
this.props.handleClose();
}
UserAction(event) {
let orderid = event.target.value;
fetch('http://localhost:54408/api/orders/change/testing-9!8-7!6/' + orderid + '/10-04-2018');
console.log("order id = " + event.target.value);
this.getUpdatedProduct();
}
getUpdatedProduct() {
console.log("fetching new product");
}
render() {
//const open = this.props.open;
const {title, plaatje, open, orderid} = this.props;
return (
<div className={'modal fade in '+(open?'show':'')} role="dialog">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button type="button" onClick={this.handleClose.bind(this)} className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 className="modal-title">{title}</h4>
</div>
<div className="modal-body">
<img className="plaatjediv img-responsive" src={plaatje} />
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default"
onClick={this.handleClose.bind(this)}>Sluiten
</button>
<button type="button" onClick={this.UserAction.bind()} value={orderid} className="btn btn-primary">Product niet aanwezig</button>
</div>
</div>
</div>
</div>
)
}
}
export default ProductModal;

React: change order list when button clicked

I am making my first app with Javascript and React and started with a page which views a shopping list. It gets the items from an api call.
If the user clicks on the button 'done' (or should I use an checkbox?) This product should go to the bottom of the list (and be grayed out with css but thats not the problem).
The problem is, I have no clue how to do this. Can anyone help me out a bit?
This is my code:
import React from 'react';
//import image from '../images/header.png';
//import Collapsible from './Collapsible';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
orders: []
}
}
componentWillMount() {
localStorage.getItem('orders') && this.setState({
orders: JSON.parse(localStorage.getItem('orders')),
isLoading: false
})
}
componentDidMount() {
if (!localStorage.getItem('orders')){
this.fetchData();
} else {
console.log('Using data from localstorage');
}
}
fetchData() {
fetch('http://localhost:54408/api/orders/all/15-03-2018')
.then(response => response.json())
.then(parsedJSON => parsedJSON.map(product => (
{
productname: `${product.ProductName}`,
image: `${product.Image}`,
quantity: `${product.Quantity}`,
isconfirmed: `${product.IsConfirmed}`,
orderid: `${product.OrderId}`
}
)))
.then(orders => this.setState({
orders,
isLoading: false
}))
.catch(error => console.log('parsing failed', error))
}
componentWillUpdate(nextProps, nextState) {
localStorage.setItem('orders', JSON.stringify(nextState.orders));
localStorage.setItem('ordersDate', Date.now());
}
render() {
const {isLoading, orders} = this.state;
return (
<div>
<header>
<img src="/images/header.jpg"/>
<h1>Boodschappenlijstje <button className="btn btn-sm btn-danger">Reload</button></h1>
</header>
<div className={`content ${isLoading ? 'is-loading' : ''}`}>
<div className="panel">
{
!isLoading && orders.length > 0 ? orders.map(order => {
const {productname, image, quantity, orderid} = order;
return<div className="product" key={orderid}>
<div className="plaatjediv">
<img className="plaatje" src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
<p>ID: {orderid}</p>
</div>
<div className="bdone">
<button className="btn btn-sm btn-default btndone">Done</button>
</div>
</div>
}) : null
}
</div>
<div className="loader">
<div className="icon"></div>
</div>
</div>
</div>
);
}
}
export default App;
You can achieve by using this :
this.handleDoneAction = event = > {
let itemIndex = event.target.getAttribute("data-itemIndex");
let prevOrders = [...this.state.orders];
var itemToMoveAtLast = prevOrders.splice(itemIndex, 1);
var updatedOrderList = prevOrders.concat(itemToMoveAtLast);
this.setState({order: updatedOrderList})
}
I have attach an event handler on the button handleDoneAction.
<button className="btn btn-sm btn-default btndone" data-itemIndex={index} onClick={this.handleDoneAction}>Done</button>
the attribute data-itemIndex is the index of the object in orders array.
And your map function will be like this:
orders.map((order, index) => {
//content
})
ANd for the different style effects on the done products, I will suggest you to use different array for all done products.

Resources