Hi I have a React frontend and Rails backend. And something very weird is happening when I do. PATCH request on the backend. I get some routing error in my rails log.No idea why this is happening pls help me out.... heres my code Here is the error
Started PATCH "/reservation/2" for 127.0.0.1 at 2022-09-01 00:32:51 0530
ActionController::RoutingError (No route matches [PATCH] "/reservation/2"):
Started GET "/me" for 127.0.0.1 at 2022-09-01 00:32:51 0530
Processing by UsersController#show as */*
hello
4
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 4], ["LIMIT", 1]]
↳ app/controllers/application_controller.rb:9:in `authorize'
[active_model_serializers] Rendered UserSerializer with ActiveModelSerializers::Adapter::Attributes (0.17ms)
Completed 200 OK in 2ms (Views: 0.4ms | ActiveRecord: 0.3ms | Allocations: 832)
Started GET "/reservations" for 127.0.0.1 at 2022-09-01 00:32:51 0530
Processing by ReservationsController#index as */*
hello
4
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 4], ["LIMIT", 1]]
↳ app/controllers/application_controller.rb:9:in `authorize'
Reservation Load (0.2ms) SELECT "reservations".* FROM "reservations" WHERE "reservations"."user_id" = $1 [["user_id", 4]]
↳ app/controllers/reservations_controller.rb:4:in `index'
[active_model_serializers] Restaurant Load (0.1ms) SELECT "restaurants".* FROM "restaurants" WHERE "restaurants"."id" = $1 LIMIT $2 [["id", 30], ["LIMIT", 1]]
[active_model_serializers] ↳ app/controllers/reservations_controller.rb:4:in `index'
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModelSerializers::Adapter::Attributes (1.53ms)
Completed 200 OK in 4ms (Views: 2.3ms | ActiveRecord: 0.5ms | Allocations: 2873)
Routes.rb
Rails.application.routes.draw do
resources :reservations,only: [:index,:show,:create,:update,:destroy]
resources :reviews,only: [:index,:create,:destroy]
resources :restaurants,only: [:index]
post "/signup", to: "users#create"
get "/me", to: "users#show"
post "/login", to: "sessions#create"
delete "/logout", to: "sessions#destroy"
end
The controller
def update
reservation = Reservation.find_by(id: params[:id])
reservation.update!(reservation_params)
render json: reservation,status: :ok
end
def reservation_params
params.permit(:name, :date, :time, :num, :contact, :occasion,:user_id,:restaurant_id)
end
and the front end App.js
function App() {
const [user, setUser] = useState(null);
const[reviews,setReviews]=useState([]);
const[current,setCurrent]=useState({});
useEffect(() => {
document.title = "Nyc";
}, []);
useEffect(() => {
// auto-login
fetch("/me", { credentials: "same-origin" }).then((r) => {
if (r.ok) {
r.json().then((user) => setUser(user));
}
});
}, [setUser]);
function handleAddReviews(newReview) {
setReviews([...reviews, newReview]);
}
if (!user) return <Loggin error={'please login'} onLogin={setUser} />;
return (
<Cont.Provider value={ {current,setCurrent}}>
<div className="App">
<Navbar user={user} setUser={setUser} />
<Routes>
<Route exact path="/blogs" element={<Blogs />} />
<Route exact path="/myreservations" element={<MyReservations user={user} />} />
<Route exact path="/restaurants/:id" element= {<RestaurantInfo handleAddReviews={handleAddReviews} user={user} reviews={reviews} setReviews={setReviews}/>} />
<Route exact path="/restaurants" element={<Restaurants />} />
<Route exact path="/about" element={<About user={user} />} />
</Routes>
</div>
</Cont.Provider>
);
}
MyReservations.js
import {useEffect, useState } from "react";
import ReservationCard from "./ReservationCard";
function MyReservations({user}){
// const[editMode,setEditForm]=useState(false);
const[reservations,setReservations]=useState([]);
useEffect(()=>{
fetch("/reservations")
.then(res=>res.json())
.then(reservationData=>{
setReservations(reservationData)
})
},[])
// console.log("reservations",reservations)
function handleUpdateReservation(updatedReservation) {
const updatedReservations = reservations.map((reservation) => {
if (reservation.id === updatedReservation.id) {
return updatedReservation;
} else {
return reservation;
}
});
setReservations(updatedReservations);
}
function handleCancel(reservationtodelete){
const newReservations=reservations.filter(r=>r.id !== reservationtodelete)
setReservations(newReservations)
}
// const renderReservations=reservations.map((reservation)=>(
// <ReservationCard key={reservation.id} reservation={reservation} handleCancel={handleCancel} />
// ))
const renderReservations=reservations.map((reservation)=>(
<ReservationCard key={reservation.id} reservation={reservation} handleCancel={handleCancel} onUpdateReservation={handleUpdateReservation} />
))
return(
<>
{renderReservations}
</>
)
}
export default MyReservations;
ReservationCard.js
function ReservationCard({reservation,handleCancel,onUpdateReservation}){
const{ name, date, time, num, contact, occasion}=reservation;
const [isEditing, setIsEditing] = useState(false);
const handleReservationUpdate = (updatedReservation) => {
setIsEditing(false);
onUpdateReservation(updatedReservation);
};
function handleDeleteClick() {
fetch(`/reservations/${reservation.id}`, {
method: "DELETE",
})
handleCancel(reservation.id)
}
return(
<>
{isEditing ?( <EditReservationForm reservation={reservation} onUpdateReservation={handleReservationUpdate} />) :(
<div>
<h2>{name}</h2>
<h2>{date}</h2>
<h2>{time}</h2>
<h2>{num}</h2>
<h2>{contact}</h2>
<h2>{occasion}</h2>
<h3>For-{reservation.restaurant.name}</h3>
<button onClick={() => setIsEditing((isEditing) => !isEditing)}>
Edit
</button>
<button onClick={handleDeleteClick}>Cancel Booking</button>
</div>
)}
</>
)
}
export default ReservationCard;
EditReservationForm
function EditReservationForm({reservation,onUpdateReservation}){
const{ name, date, time, num, contact, occasion}=reservation;
const[updateName,setUpdatedName]=useState(name);
const[updateDate,setUpdatedDate]=useState(date);
const[updateTime,setUpdatedTime]=useState(time);
const[updateNum,setUpdatedNum]=useState(num);
const[updateContact,setUpdatedContact]=useState(contact);
const[updateOccasion,setUpdatedOccasion]=useState(occasion);
function handleEditClick() {
fetch(`/reservation/${reservation.id}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ name:updateName,date:updateDate, time:updateTime,num:updateNum,contact:updateContact,occasion:updateOccasion}),
})
.then((r) => r.json())
.then((updatedReservation) => {
onUpdateReservation(updatedReservation);
});
}
return(
<>
<h2>Modify Reservation</h2>
<form onSubmit={handleEditClick} >
<div >
<label htmlFor="name" >Name</label>
<input type="text" name="name" value={updateName} onChange={(e) => setUpdatedName(e.target.value)} placeholder="name" />
</div>
<div >
<label htmlFor="date" >Date</label>
<input type="date" name="date" value={updateDate} onChange={(e) => setUpdatedDate(e.target.value)} placeholder="date" />
</div>
<div >
<label htmlFor="time" >Time</label>
<input type="time" name="time" value={updateTime} onChange={(e) => setUpdatedTime(e.target.value)} placeholder="time" />
</div>
<div >
<label htmlFor="num" >Num</label>
<input type="number" name="num" value={updateNum} onChange={(e) => setUpdatedNum(e.target.value)} placeholder="num" />
</div>
<div >
<label htmlFor="date" >Contact</label>
<input type="tel" name="contact" value={updateContact} onChange={(e) => setUpdatedContact(e.target.value)} placeholder="contact" />
</div>
<div >
<label htmlFor="occasion" >Occasion</label>
<input type="text" name="occasion" value={updateOccasion} onChange={(e) => setUpdatedOccasion(e.target.value)} placeholder="occasion" />
</div>
<button type="submit">Update Reservation</button>
</form>
</>
)
}
export default EditReservationForm;
CodePudding user response:
You haven't /reservation/2
route. That's right
You have /reservations/2
because you have :update
in your resources :reservations
You need to change this path in your fetch
in JS
To see all the routes run
rails routes
CodePudding user response:
You need to show your routes.rb
file, It seems like you don't have any routes defined that handle a PATCH request at "/reservation/2"