Cannot Update Data in Reactjs+Laravel App - reactjs

I have an offer-adding feature in my app. my front end is on react js and the backend is on Laravel. I have created a route to update the data and called it using Axios but I don't know what I am doing wrong. Any type of Help will be appreciated Thank You!
this is my route in api.php
Route::put('offers/{id}', 'Api\Offer\OfferController#update');
This is my OfferController
public function update(Request $request, $id)
{
$offer = Offer::findOrFail($id);
$offer->update($request->all());
return $offer;
}
This is my API call function for update
export const updateItem = (offertitle,offerdescription,vid,id) => {
return axios
.put(
`/api/offers/${id}`,
{
offertitle:offertitle,
offerdescription:offerdescription,
vid:vid
},
{
headers: { 'Content-Type': 'application/json' }
}
)
.then(function (response) {
console.log(response)
})
}
OfferComponent
export default class Offer extends React.Component{
constructor() {
super()
this.state = {
offertitle: '',
offerdescription: '',
editDisabled: false,
offers: [],
redirect: false,
vid:'',
offerid:''
}
this.onSubmit = this.onSubmit.bind(this)
this.onChange = this.onChange.bind(this)
}
componentDidMount() {
this.getAll()
if (sessionStorage.getItem('user')) {
vendorprofile().then(res => {
this.setState({
vid: res.user.vid
})
})
}
else {
this.setState({ redirect: true });
}
}
onChange = e => {
this.setState({
[e.target.name]: e.target.value
})
}
getAll = () => {
getList().then(data => {
this.setState(
{
offertitle:'',
offerdescription:'',
offers: [...data]
},
() => {
console.log(this.state.offers)
}
)
})
}
onSubmit = e => {
e.preventDefault()
addItem({offertitle: this.state.offertitle, offerdescription: this.state.offerdescription,vid: this.state.vid}).then(() => {
this.getAll()
})
this.setState({
offertitle: '',
offerdescription:'',
vid:''
})
}
onUpdate = e => {
e.preventDefault()
updateItem({ offertitle: this.state.offertitle, offerdescription: this.state.offerdescription, offerid: this.state.offerid, vid: this.state.vid }).then(() => {
this.getAll()
})
this.setState({
offertitle: '',
offerdescription: '',
vid:'',
editDisabled: ''
})
this.getAll()
}
onEdit = (offerid, e) => {
e.preventDefault()
var data = [...this.state.offers]
data.forEach((offers,index) => {
if (offers.offerid === offerid) {
this.setState({
offerid: offers.offerid,
offertitle: offers.offertitle,
offerdescription: offers.offerdescription,
editDisabled: true
})
}
})
}
onDelete = (val, e) => {
e.preventDefault()
deleteItem(val)
var data = [...this.state.offers]
data.filter(function (offers, index) {
if (offers.offerid === val) {
data.splice(index, 1)
}
return true
})
this.setState({ offers: [...data] })
}
render() {
if (this.state.redirect) {
return (
<Redirect to="/stsignin" />
)
}
return (
<div>
<Pane />
<div className="container" style={{ marginTop : 150}}>
<form>
<div className="form-group">
<div className="row">
<div className="col-md-12">
<label>OFFER TITLE</label>
<input
type="text"
className="form-control"
id="offertitle"
name="offertitle"
value={this.state.offertitle || ''}
onChange={this.onChange.bind(this)}
/>
<label>OFFER DESCRIPTION</label>
<input
type="text"
className="form-control"
id="offerdescription"
name="offerdescription"
value={this.state.offerdescription || ''}
onChange={this.onChange.bind(this)}
/>
</div>
</div>
</div>
{!this.state.editDisabled ? (
<button
type="submit"
onClick={this.onSubmit.bind(this)}
className="btn btn-success btn-block"
>
Submit
</button>
) : (
''
)}
{this.state.editDisabled ? (
<button
type="submit"
onClick={this.onUpdate.bind(this)}
className="btn btn-primary btn-block"
>
Update
</button>
) : (
''
)}
</form>
<table className="table">
<tbody>
<tr>
<th>OFFER_TITLE</th>
<th>OFFER_DESCRIPTION</th>
</tr>
{this.state.offers.map((offers,index) => (
<tr key={index}>
<td className="text-left">{offers.offertitle}</td>
<td className="text-left">{offers.offerdescription}</td>
<td className="text-right">
<button
href=""
className="btn btn-info mr-1"
disabled={this.state.editDisabled}
onClick={this.onEdit.bind(
this,
offers.offerid
)}
>
Edit
</button>
<button
href=""
className="btn btn-danger"
disabled={this.state.editDisabled}
onClick={this.onDelete.bind(
this,
offers.offerid
)}
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)
}
}```

Error 404 Might mean that the offer specified in the link doesn't exist and thus can't be updated. Check if you have an order with the id that you are searching in the database. However, posting your entire response might show us more information. A simple 404 is pretty vague, you might want to copy paste that in.
That being said, i recommend using Eloquent for these kind of routes. You can basically change your route to /offers/{offer} and then instead of typing $id as a variable for that function and then finding the offer by id you can just simply query $offer->update(your_data) and it would work as expected. It is exactly the same thing but just keeps it cleaner and removes any basic mistakes

Related

Use react modal bootstrap to edit items in a map list

My application have two components, one renders a list and the other render the form:
I would like to use the same component to create new categories and also to edit the categories that already exists.
The create categories is working fine already, but to edit, needs to pass the id of the category in the list to the form inside the modal, as i am new into react, i would like some help. Thanks in advance.
The list file is called,
Categories.jsx
import React, { Component } from 'react'
import { Alert, Modal, Button } from "react-bootstrap";
import Datatable from '../../../globalcomponents/datatable/Datatable';
import CategoryForm from './CategoryForm';
const Api = require('../../api/CategoriesApi.js')
class Categories extends Component {
constructor(props) {
super(props)
this.state = {
categories: [],
isLoaded: false,
error: null,
isOpen: false
}
}
openModal = () => this.setState({ isOpen: true });
closeModal = () => this.setState({ isOpen: false });
componentDidMount() {
Api.getCategories()
.then(response => {
const [error, data] = response
if (error) {
this.setState({
isLoaded: true,
categories: [],
error: data
})
} else {
this.setState({
isLoaded: true,
categories: data
})
}
})
}
render() {
const { error, isLoaded, categories } = this.state
if (error) {
return (
<Alert color="danger">
Error: {error}
</Alert>
)
} else if (!isLoaded) {
return (
<Alert color="primary">
Loading...
</Alert>
)
} else {
return (
<>
<Button className="float-right" variant="primary" onClick={this.openModal}>
Adicionar
</Button>
<h4 className="mt-4 mb-4">Categorias de investimentos</h4>
<Datatable>
<table className="table table-striped my-4 w-100">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Url (Slug)</th>
<th></th>
</tr>
</thead>
<tbody>
{categories.map(category => (
<tr key={category.id}>
<td>{category.id}</td>
<td>{category.title}</td>
<td>{category.slug}</td>
<td>
<Button className="float-right mr-2" variant="primary" onClick={this.openModal}>
Modal Edit
</Button>
</td>
</tr>
))}
</tbody>
</table>
</Datatable>
<Modal show={this.state.isOpen} onHide={this.closeModal}>
<Modal.Header closeButton>
<Modal.Title>Adicionar / Editar</Modal.Title>
</Modal.Header>
<Modal.Body>
<CategoryForm />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.closeModal}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
)
}
}
}
export default Categories
The form file is used to create or edit categories. And it is called:
CategoryForm.jsx
import React, { Component } from 'react'
import { Redirect } from 'react-router'
import { Row, Col, Alert, Button, Form, FormGroup, Label, Input } from 'reactstrap'
const Api = require('../../api/CategoriesApi.js')
class CategoryForm extends Component {
constructor(props) {
super(props)
this.state = {
category: {
id: this.getCategoryId(props),
title: '',
slug: '',
},
redirect: null,
errors: []
}
this.setTitle = this.setTitle.bind(this)
this.setSlug = this.setSlug.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
getCategoryId(props) {
try {
return props.match.params.id
} catch (error) {
return null
}
}
setTitle(event) {
let newVal = event.target.value || ''
this.setFieldState('title', newVal)
}
setSlug(event) {
let newVal = event.target.value || ''
this.setFieldState('slug', newVal)
}
setFieldState(field, newVal) {
this.setState((prevState) => {
let newState = prevState
newState.category[field] = newVal
return newState
})
}
handleSubmit(event) {
event.preventDefault()
let category = {
title: this.state.category.title,
slug: this.state.category.slug,
}
Api.saveCategory(category, this.state.category.id)
.then(response => {
const [error, errors] = response
if (error) {
this.setState({
errors: errors
})
} else {
this.setState({
// reload categories
redirect: '/admin'
})
}
})
}
componentDidMount() {
if (this.state.category.id) {
Api.getCategory(this.state.category.id)
.then(response => {
const [error, data] = response
if (error) {
this.setState({
errors: data
})
} else {
this.setState({
category: data,
errors: []
})
}
})
}
}
render() {
const { redirect, category, errors } = this.state
if (redirect) {
return (
<Redirect to={redirect} />
)
} else {
return (
<>
<Row>
<Col>
{errors.length > 0 &&
<div>
{errors.map((error, index) =>
<Alert color="danger" key={index}>
{error}
</Alert>
)}
</div>
}
<Form onSubmit={this.handleSubmit}>
<FormGroup>
<Label for="title">Title</Label>
<Input type="text" name="title" id="title" value={category.title} placeholder="Enter title" onChange={this.setTitle} />
</FormGroup>
<FormGroup>
<Label for="slug">Slug</Label>
<Input type="text" name="slug" id="slug" value={category.slug} placeholder="Enter slug" onChange={this.setSlug} />
</FormGroup>
<Button color="success">Submit</Button>
</Form>
</Col>
</Row>
</>
)
}
}
}
export default CategoryForm
You are not passing the id in Categories.jsx. either you can set the id in the history state or do pass it by component
prop drill.
setting the state in history Programmatically set params in React Router v4
Or You can do Pass the id to the Component and handle in Component
DidMount Event.
here is the code sandbox link
Categories.jsx
/** Create a id variable in state. **/
class Categories extends Component {
constructor(props) {
super(props);
this.state = {
categories: [],
isLoaded: false,
error: null,
isOpen: false,
--> id: null <--
};
}
/** change the openModal code to something like this. **/
openModal = (id) => {
this.setState( (prev) => {
const state = prev.state;
return { ...state, id: id, isOpen:true };
});
};
/** while Onclick set the id in the state. **/
<Datatable>
<table className="table table-striped my-4 w-100">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Url (Slug)</th>
<th></th>
</tr>
</thead>
<tbody>
{categories.map((category) => (
<tr key={category.id}>
<td>{category.id}</td>
<td>{category.title}</td>
<td>{category.slug}</td>
<td>
<Button
className="float-right mr-2"
variant="primary"
--> onClick={() =>this.openModal(category.id)}
>
Modal Edit
</Button>
</td>
</tr>
))}
</tbody>
</table>
</Datatable>
/** Pass the id prop for CategoryForm Component in Modal body from the state. **/
<Modal show={this.state.isOpen} onHide={this.closeModal} >
<Modal.Header closeButton>
<Modal.Title>Adicionar / Editar</Modal.Title>
</Modal.Header>
<Modal.Body>
--> <CategoryForm id={this.state.id || null} />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick= {this.closeModal}>
Close
</Button>
</Modal.Footer>
</Moddal>
CategoryForm.jsx
In the componentDidMount conditionally check if there is id variable in props.
componentDidMount() {
**// Check if props.id is available**
if (** this.state.category.id || this.props.id **) {
**const id = this.state.category.id || this.props.id;**
Api.getCategory(id).then((response) => {
const [error, data] = response;
if (error) {
this.setState({
errors: data
});
} else {
alert(id);
this.setState({
category: data,
errors: []
});
}
});
}
}
You can add more state to Categories to keep track of additional data about the modal.
*I have only included the highlights in the code here; lots was left out for brevity.
In Categories.jsx:
constructor(props) {
super(props)
this.state = {
categories: [],
isLoaded: false,
error: null,
isOpen: false,
modalData: null,
}
}
openModal = (modalData) => this.setState({ isOpen: true, modalData });
closeModal = () => this.setState({ isOpen: false, modalData: null });
//'create' dialog button
<Button className="float-right" variant="primary" onClick={e => this.openModal({modalType: 'create'})}>
Adicionar
</Button>
//here are the table rows:
{categories.map(category => (
<tr key={category.id}>
<td>{category.id}</td>
<td>{category.title}</td>
<td>{category.slug}</td>
<td>
<Button className="float-right mr-2" variant="primary" onClick={e => this.openModal({modalType: 'edit', categoryId: category.id})}>
Modal Edit
</Button>
</td>
</tr>
))}
//the modal. pass modalData as a prop:
<Modal show={this.state.isOpen} onHide={this.closeModal}>
<Modal.Header closeButton>
<Modal.Title>Adicionar / Editar</Modal.Title>
</Modal.Header>
<Modal.Body>
<CategoryForm modalData={modalData} />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.closeModal}>
Close
</Button>
</Modal.Footer>
</Modal>
In CategoryForm.jsx:
//get id from props:
getCategoryId(props) {
return (props.modalData.modalType === 'edit') ? props.modalData.categoryId : false;
//I don't know if this needs to be here:
//try {
// return props.match.params.id
//} catch (error) {
// return null
//}
}
You might have to refactor CategoryForm.jsx. For instance, the categoryId is now a prop, so it doesn't need to be duplicated in state.

When trying to create new item in MERN stack, getting TypeError: this.props.meals.map is not a function

I've created a MERN with redux application where users can order a meal from a menu. In the admin side, I am providing delete and add functions so the meals on the menu can be changed all in the same page. I have managed to get the delete meal item to work, but I am getting the following error when I try and add a new meal item:
My redux action is as follows:
export const createMeal = (meal) => (dispatch) => {
fetch("api/meals", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(meal),
})
.then((res) => res.json())
.then((data) => {
dispatch({ type: CREATE_MEAL, payload: data });
});
};
In my server file, I have the following endpoint created in Express:
app.post("/api/meals", async (req, res) => {
if (!req.body.title) {
return res.send({ message: "Data is required." });
}
const newMeal = new Meal(req.body);
const savedMeal = await newMeal.save();
res.send(savedMeal);
});
My UpdateMenuScreen is as follows:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchMeals, deleteMeal, createMeal } from "../actions/mealActions";
class UpdateMenuScreen extends Component {
constructor(props) {
super(props);
this.state = {
meal: null,
showAddMenu: false,
title: "",
};
}
componentDidMount() {
this.props.fetchMeals();
}
componentDidUpdate() {
this.props.fetchMeals();
}
handleInput = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
createMeal = (e) => {
e.preventDefault();
const meal = {
title: this.state.title,
};
this.props.createMeal(meal);
};
deleteMeal(id) {
this.props.deleteMeal(id);
}
render() {
return (
<div>
<h3>Current Menu</h3>
{!this.props.meals ? (
<div>Loading...</div>
) : (
<ul className="meals">
{this.props.meals.map((meal) => (
<li key={meal._id}>
<div className="meal">
<p>{meal.title}</p>
<button
className="button"
onClick={() => this.props.deleteMeal(meal._id)}
>
Delete
</button>
</div>
</li>
))}
</ul>
)}
<button
onClick={() => {
this.setState({ showAddMenu: true });
}}
>
Add New Menu Item
</button>
{this.state.showAddMenu && (
<div className="cart">
<form onSubmit={this.createMeal}>
<ul className="form-container">
<li>
<label>Menu Item Title:</label>
<input
name="title"
type="text"
required
onChange={this.handleInput}
></input>
</li>
<li>
<button className="button primary" type="submit">
Save New Menu Item
</button>
</li>
</ul>
</form>
</div>
)}
</div>
);
}
}
export default connect((state) => ({ meals: state.meals.items }), {
fetchMeals,
deleteMeal,
createMeal,
})(UpdateMenuScreen);
Can anyone see what I'm missing? Or is it not possible to do this all on the same page?
EDIT:
I've console logged this.props.meals in ComponentDidMount and got the following results:
My mealsReducer is as follows:
const { FETCH_MEALS, DELETE_MEAL, CREATE_MEAL } = require("../types");
export const mealsReducer = (state = {}, action) => {
switch (action.type) {
case FETCH_MEALS:
return { items: action.payload };
case DELETE_MEAL:
return { items: action.payload };
case CREATE_MEAL:
return { items: action.payload };
default:
return state;
}
};
I also get this underneath my original error, could it be something in my mealActions that I don't have correct?
Please go to your reducer of meals and define the initial state of meals to []
This should fix your error.
render() {
const { meals = [] } = this.props // default to empty array when it's undefined
return (
<div>
<h3>Current Menu</h3>
{!meals.length ? (
<div>Loading...</div>
) : (
<ul className="meals">
{meals.map((meal) => (
<li key={meal._id}>
<div className="meal">
<p>{meal.title}</p>
<button
className="button"
onClick={() => this.props.deleteMeal(meal._id)}
>
Delete
</button>
</div>
</li>
))}
</ul>
)}
<button
onClick={() => {
this.setState({ showAddMenu: true });
}}
>
Add New Menu Item
</button>
{this.state.showAddMenu && (
<div className="cart">
<form onSubmit={this.createMeal}>
<ul className="form-container">
<li>
<label>Menu Item Title:</label>
<input
name="title"
type="text"
required
onChange={this.handleInput}
></input>
</li>
<li>
<button className="button primary" type="submit">
Save New Menu Item
</button>
</li>
</ul>
</form>
</div>
)}
</div>
);
}
}

Set Component State Synchronously on ComponentWIllReveiveProps

I am passing props from Dashboard component to Modal Component when I click an item in the Dahboard table row. When the row is clicked, the data in that row is passed as props to the modal and the modal set its state on ComponenentWIllReceiveProps synchronously so it renders in the Modal Input box and textarea
How do I set the state in modal with props passed down from Dashboard.js and render them in the modal inpputbox and textarea
Dashboard.js
import React, { Component } from 'react'
import Modal from '../Modal/Modal'
import add from '../../images/add.png'
import addSelected from '../../images/addSelected.png'
import './Dashboard.css'
const TableRow = ({ row, openQuoteDetails, deleteQuote }) => (
<tr>
<th scope="row" onClick={openQuoteDetails}>{row.author}</th>
<td onClick={openQuoteDetails}>{row.quote}<small id="admin" className="form-text text-muted">{row.admin}</small></td>
<td><i className="fa fa-close" onClick={deleteQuote}></i></td>
</tr>
)
const Table = ({ data, openQuoteDetails, deleteQuote }) => (
<table className="table table-hover">
<thead>
<tr className="table-active">
<th scope="col">Author</th>
<th scope="col">Quote</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{data.map((row, index) =>
<TableRow key={index} row={row} openQuoteDetails={() => openQuoteDetails(row, index)} deleteQuote={() => deleteQuote(row, index)} />
)}
</tbody>
</table>
)
class Dashboard extends Component {
constructor() {
super()
this.state = {
quotes: [
{
"quote": "Our industry does not respect tradition - it only respects innovation.",
"author": "Satya Nadella",
"admin": "Joseph Akayesi"
},
{
"quote": "Engineering is the closest thing to magic that exists in the world.",
"author": "Elon Musk",
"admin": "Joseph Akayesi"
},
{
"quote": "For me, it matters that we drive technology as an equalizing force, as an enabler for everyone around the world.",
"author": "Sundar Pichai",
"admin": "Yasmin Adams"
}
],
addSource: add,
isModalOpen: false,
index: '',
author: '',
quote: ''
}
}
onAddMouseOver = () => {
this.setState({ addSource: addSelected })
}
onAddMouseOut = () => {
this.setState({ addSource: add })
}
toggleModalOpenOrClose = () => {
this.setState({ isModalOpen: !this.state.isModalOpen })
this.setState({ index: '' })
this.setState({ author: '' })
this.setState({ quote: '' })
}
openQuoteDetails = (row, index) => {
this.setState({ isModalOpen: true });
this.setState({ index: index, author: row.author, quote: row.quote })
}
deleteQuote = (row, index) => {
this.setState({ isModalOpen: false })
console.log('Row deleted')
console.log(this.state.quotes.splice(index, 1))
}
render() {
return (
<div className='pt-3'>
<Table
data={this.state.quotes}
openQuoteDetails={this.openQuoteDetails}
deleteQuote={this.deleteQuote} />
<div className='text-center align-items-center justify-content-centerpt-5'>
<a href='#add' onClick={this.toggleModalOpenOrClose}>
<img src={this.state.addSource} className='addButton mx-1' alt="add" onMouseOver={this.onAddMouseOver} onMouseOut={this.onAddMouseOut} />
</a>
</div>
<Modal
isModalOpen={this.state.isModalOpen}
toggleModalOpenOrClose={this.toggleModalOpenOrClose}
data={this.state}
onInputChange={this.onInputChange}
addNewQuote={this.addNewQuote}
updateExistingQuote={this.updateExistingQuote} />
</div>
)
}
}
export default Dashboard
Modal.js
import React, { Component } from 'react'
import './Modal.css'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { addQuote } from '../../actions/quoteActions'
class Modal extends Component {
constructor(props) {
super(props)
this.state = {
id: '',
author: '',
quote: '',
errors: {}
}
}
onInputChange = (event) => {
this.setState({ [event.target.id]: event.target.value })
}
switchSaveChangesAction = () => {
return this.props.state.index ? this.addNewQuote : this.updateExistingQuote
}
addNewQuote = () => {
const { user } = this.props.auth
const newQuote = {
admin: user.id,
quote: this.state.quote,
author: this.state.author,
}
console.log('Add New')
console.log(newQuote)
}
componentWillReceiveProps(nextProps) {
console.log('receive props')
if(nextProps.author !== this.props.author){
this.setState({ author: nextProps.author})
}
// this.setState({ id: this.props.data.index })
console.log(nextProps)
this.setState({ author: this.props.data.author }, () => console.log(this.state.author))
console.log(this.state)
}
updateExistingQuote = (index) => {
console.log('Update Existing')
console.log(this.props.state.author)
console.log(this.props.state.quote)
console.log(this.props.state.index)
}
render() {
let showOrHideModal = this.props.isModalOpen ? 'modal d-block' : 'modal d-none'
// let selectedQuoteDetails = {
// id: this.props.data.index ? this.props.data.index : '',
// author: this.props.data.author ? this.props.data.author : '',
// quote: this.props.data.quote ? this.props.data.quote : ''
// };
// let modalInputValue = selectedQuoteDetails ? selectedQuoteDetails : this.state
let saveChangesAction = this.props.data.index >= 0 ? this.updateExistingQuote : this.addNewQuote
return (
<div className={showOrHideModal}>
<div className='modal-dialog' role='document'>
<div className='modal-content'>
<div className='modal-header bg-light'>
<h5 className='modal-title'><b>Add a Quote</b></h5>
<button type='button' className='close' data-dismiss='modal' aria-label='Close' onClick={this.props.toggleModalOpenOrClose}>
<span aria-hidden='true'>×</span>
</button>
</div>
<div className='modal-body'>
<div className='form-group'>
<label htmlFor='author'>Author</label>
<input type='text' className='form-control' id='author' aria-describedby='emailHelp' placeholder='Enter author' onChange={this.onInputChange} defaultValue={this.state.author} />
</div>
<div className='form-group'>
<label htmlFor='quote'>Quote</label>
<textarea className='form-control' id='quote' rows='3' placeholder='Enter quote' onChange={this.onInputChange} value={this.state.quote}></textarea>
</div>
</div>
<div className='modal-footer'>
<button type='button' className='btn btn-primary' onClick={saveChangesAction}>Save changes</button>
<button type='button' className='btn btn-secondary' data-dismiss='modal' onClick={this.props.toggleModalOpenOrClose}>Close</button>
</div>
</div>
</div>
</div>
)
}
}
Modal.propTypes = {
addQuote: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
}
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors
})
export default connect(mapStateToProps, { addQuote })(Modal)

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.

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