My delete method for bookmarks does not seem to work, is this a problem with rails 7 or am I doing something wrong. It states that there is no Get method. I am using turbo-pack methods vs webpack
below is all relevant documents
routes.rb
Rails.application.routes.draw do
root 'lists#index'
resources :lists, except: [:edit, :update] do
resources :bookmarks, only: [:new, :create]
end
resources :bookmarks, only: :destroy
end
bookmarks_controller.rb
class BookmarksController < ApplicationController
before_action :set_bookmark, only: :destroy
before_action :set_list, only: [:new, :create]
def new
#bookmark = Bookmark.new
end
def create
#bookmark = Bookmark.new(bookmark_params)
#bookmark.list = #list
if #bookmark.save
redirect_to list_path(#list)
else
render :new
end
end
def destroy
#bookmark.destroy
redirect_to list_path(#bookmark.list), status: :see_other
end
private
def bookmark_params
params.require(:bookmark).permit(:comment, :movie_id)
end
def set_bookmark
#bookmark = Bookmark.find(params[:id])
end
def set_list
#list = List.find(params[:list_id])
end
end
show.html.erb
<div class="row">
<% #list.movies.each do |movie|%>
<% bookmark = Bookmark.find_by(list: #list, movie: movie) %>
<div class="col-12 col-lg-4">
<div class="card text-center text-white" style="background-color:#ffa82e00;">
<div class="card photo" class="card-img-top">
<%= image_tag(movie.poster_url, class: "card-img-top" )%>
<div class="card-body">
<h5 class="card-title text-dark"><%= movie.title%></h5>
<p class="card-text text-body"><%= movie.overview %></p>
<p class="card-text text-body"><%= bookmark.comment%></p>
<%= link_to "delete", bookmark_path(bookmark), data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to remove #{movie.title} from your #{#list.name} list"}, class: 'text-danger' %>
</div>
</div>
</div>
</div>
<% end %>
<%= link_to "Bookmark now", new_list_bookmark_path(#list), class: "btn btn-primary" %>
Just use button_to instead which creates an actual form element and works even without relying on JavaScript:
<%= button_to "delete",
bookmark,
data: {
turbo_confirm: "Are you sure you want to remove #{movie.title} from your #{#list.name} list"
},
method: :delete,
class: 'text-danger'
%>
This has additional benefits to accessibility and the historical reasons why Rails used links and JS to turn clicking a link into a form submission are largely irrelevant in 2023. This is a feature that Turbo just provides for backwards compatibly.
As to why it doesn't work:
Turbo isn't properly included.
You have an error in your javascript thats preventing the event handler from doing its job.
You have a conflicting event handler.
It's the wrong phase of the planets...
I think it's because you are using link_to which will send a GET request. Try using button_for instead.
Related
I am creating a page where a logged in user can view a list of their upcoming reservations and cancel a reservation if need be.
I've tried this as the destroy function:
def destroy
#trip = Reservation.find(params[:id])
#trip.destroy
flash[:alert] = "This booking has been cancelled."
redirect_to your_trips_path
end # destroy/ cancel a booking
which produces the following error
undefined method `space_reservation' for #<#<Class:0x00007f8198a191d8>:0x00007f8195341160>
Did you mean? space_reservations_url
Here is the relevant code in the controller.rb
class ReservationsController < ApplicationController
before_action :authenticate_user!
before_action :set_reservation, only: [:approve, :decline, :destroy]
...
def your_trips
#today = DateTime.now
#trips = current_user.reservations.order(start_date: :desc)
end
def destroy
#reservation = Reservation.find(params[:id])
#reservation.destroy
flash[:alert] = "This booking has been cancelled."
redirect_to your_trips_path
end # destroy/ cancel a booking
Routes.rb
resources :spaces, except: [:edit] do
member do
get 'listing'
delete :delete_image_attachment
get 'preload'
get 'preview'
get 'get_dates'
get 'get_times'
put :deactivate
put :activate
get 'browse_spaces'
end
resources :reservations, only: [:create]
resources :calendars
end
resources :reservations, only: [:approve, :decline, :destroy] do
member do
post '/approve' => "reservations#approve"
post '/decline' => "reservations#decline"
delete 'destroy'
end
end
View
<div class="panel-body">
<% #trips.each do |trip| %>
...
<div class="col-md-3 text-right trips-index">
<% if trip.start_date && trip.end_date > #today %>
<%= link_to 'Cancel',
space_reservation(trip),
method: :delete,
class:"btn btn-danger",
data: { confirm: 'Are you sure?' } %>
<% end %>
</div>
</div>
<hr/>
<% end %>
</div>
I would like for the reservation to be deleted when the cancel button is clicked and to no longer appear in the list of reservations. Does anyone spot what I did wrong in the function? Thanks.
The error message says that the space_reservation method is undefined.
In your view, you called that undefined method here:
<%= link_to 'Cancel',
space_reservation(trip),
method: :delete,
class:"btn btn-danger",
data: { confirm: 'Are you sure?' } %>
<% end %>
Assuming everything else works, if you either create the space_reservation method or find another way to resolve the issue, then it should work.
Hello I am trying to create a bookmark feature in my app. I have created the
model I want to bookmark (Hairstyle), I also have a User model and a "Saved_Hairstyle" model which is the join table in the scenario.
In my routes.rb file I added a CREATE & DELETE route. In my controller I wrote out the CREATE & DELETE methods. I then proceeded to link_to my CREATE & DELETE methods paths in my View.
I would like that when I click on the CREATE Link a POST method is fired so that I can show an element on the page as being "bookmarked" and when I click the DELETE link a DELETE method is fired so that I can show an element on the page as being "unboomarked" but they don't work.
When I do RAILS ROUTES in Rails C I can see the right pathways but when I click through the links don't do anything.
Repo for ease of understanding: https://github.com/Angela-Inniss/hair-do
routes.rb
Rails.application.routes.draw do
root to: 'pages#home'
devise_for :users
resources :hairstyles do
member do
put "like", to: "hairstyles#upvote"
put "dislike", to: "hairstyles#downvote"
end
resources :comments, only: :create
resources :saved_hairstyles, only: [:new,:create]
end
resources :saved_hairstyles, only: :destroy
resources :comments, only: :destroy
resources :hairdressers
end
class SavedHairstylesController < ApplicationController
def create
#hairstyle = Hairstyle.find(params[:hairstyle_id])
#saved_hairstyle = SavedHairstyle.new(user: current_user, hairstyle: #hairstyle)
if #saved_hairstyle.save
respond_to do |format|
format.html { redirect_to hairstyle_path(#saved_hairstyle.hairstyle) }
format.js # <-- will render `app/views/comments/create.js.erb`
end
else
respond_to do |format|
format.html { render 'hairstyles' }
format.js # <-- idem
end
end
end
end
def destroy
#saved_hairstyle = SavedHairstyle.find(params[:id])
#saved_hairstyle.destroy
#hairstyle = #saved_hairstyle.hairstyle
respond_to do |format|
format.html { redirect_to hairstyle_path(#saved_hairstyle.hairstyle }
format.js
end
end
view.html.erb
<div class="bookmark">
<% saved_hairstyle = SavedHairstyle.find_by(user: current_user, hairstyle: hairstyle.id) %>
<% if saved_hairstyle %>
<%= link_to hairstyle_saved_hairstyle_path(saved_hairstyle), method: :post do %>
<i class="fas fa-plus"></i>
<% end %>
<% else %>
<%= link_to saved_hairstyle_path(hairstyle), method: :delete do %>
<i class="fas fa-plus-circle"></i>
<% end %>
<% end %>
</div>
create.js.erb file (this is what I Would like to happen on the POST request (i have something similar for the DELETE request)
plusCircle = document.getElementById("bookmark");
plusCircle.innerHTML = `<%= link_to '#', method: :post do %>
<i class="fas fa-plus"></i>
<% end %>`
There seems to be some errors in your view logic. You are loading saved hairstyles and try to book mark it again. You're also trying to delete a saved hairstyle if it doesn't exist. The path helpers also seem to be wrong (delete method is not nested and nested resources should be plural). Maybe it should look something like this:
<div class="bookmark">
<% saved_hairstyle = SavedHairstyle.find_by(user: current_user, hairstyle: hairstyle.id) %>
<% if saved_hairstyle %>
<%= link_to saved_hairstyle_path(saved_hairstyle), method: :delete do %>
<i class="fas fa-plus-circle"></i>
<% end %>
<% else %>
<%= link_to hairstyle_saved_hairstyles_path(hairstyle), method: :post do %>
<i class="fas fa-plus"></i>
<% end %>
<% end %>
</div>
By the way, if you check your console, you probably will see some errors that would point you to the right direction. You should also move saved_hairstyle query to a helper method.
I'm working on a rails app where users can post links with title, url and an uploaded image file. When displaying this link I want to have a delete picture button next to the image so that the user may click on it to delete the image.
I don't know which is the best way to do this, but here's what I did:-
Custom method in links_controller.rb:-
def destroy_picture
#link = current_user.links.find_by(id: params[:id])
if #link.picture? && #link.user==current_user
new_link = #link.dup
new_link.picture=nil
new_link.id = #link.id
new_link.created_at = #link.created_at
new_link.updated_at = Time.now
#link.destroy
new_link.save
flash[:success]="Picture deleted successfully"
end
end
Calling the custom method in the view:-
The complete files:-
link_controller.rb
class LinksController < ApplicationController
before_action :logged_in_user, only: [:new, :create, :edit, :update, :destroy]
before_action :link_owner, only: [:edit, :update, :destroy]
def new
#link = Link.new
end
def create
#link = current_user.links.build(link_params)
if #link.save
flash[:success]= "Link submitted successfully"
redirect_to root_path
else
render 'new'
end
end
def edit
#link = current_user.links.find_by(id: params[:id])
end
def update
#link = current_user.links.find_by(id: params[:id])
if #link.update_attributes(link_params)
flash[:success] = "Link successfully edited"
redirect_to current_user
else
flash[:danger] = "Link edit failed"
render 'edit'
end
end
def destroy
#link = current_user.links.find_by(id: params[:id])
#link.destroy
flash[:success] = "Link successfully deleted"
redirect_to root_path
end
def destroy_picture
#link = current_user.links.find_by(id: params[:id])
if #link.picture? && #link.user==current_user
new_link = #link.dup
new_link.picture=nil
new_link.id = #link.id
new_link.created_at = #link.created_at
new_link.updated_at = Time.now
#link.destroy
new_link.save
flash[:success]="Picture deleted successfully"
end
end
private
def link_params
params.require(:link).permit(:title, :url, :picture)
end
def link_owner
link = Link.find(params[:id])
user = link.user
redirect_to current_user unless current_user == user
end
end
_link.html.erb:-
<div class="container">
<li class="row">
<span class="avatar">
<%= image_tag link.user.avatar_url(:thumb) if link.user.avatar? %>
</span>
<span class="content">
<b><%= link.title %></b> -
<a href=<%= link.url %>><%= link.url %></a>
</span>
<span class="image">
<%= image_tag link.picture.url if link.picture? %>
</span>
<span class="content">
<% if link.user==current_user %>
<%= link_to "Delete Picture", link_path(link),
method: :destroy_picture,
class: 'btn btn-xs btn-danger' %>
<% end %>
</span>
<span class="timestamp">
Posted <%= time_ago_in_words(link.created_at) %>
ago by <%= link_to link.user.name, user_path(link.user)%>
</span>
<span class="content">
<% if current_user==link.user %>
<%= link_to "Edit", edit_link_path(link),
class: 'btn btn-xs btn-primary' %>
<%= link_to "Delete", link_path(link),
method: :delete,
data: {:confirm => "Are you sure?"},
class: 'btn btn-xs btn-danger' %>
<% end %>
</span>
</li>
</div>
This link partial may appear either on the home page or the user's profile page.
Currently I am getting param is missing or the value is empty: link when I click on the "delete picture" button. How do I get around getting the method to be executed?
Also in general, what would be the simplest way of doing this?
Replace your link_to helper with the route which points to the method to destroy such picture, and in the method use the corresponding HTTP verb, that's delete:
<%= link_to "Delete Picture", destroy_picture_path(link), method: :delete, class: 'btn btn-xs btn-danger' %>
Be sure you're passing the link object, to receive it in your controller and find the object in the database.
Also your routes must point as a delete action to your method:
delete '/links/:id', to: 'links#destroy_picture', as: 'destroy_picture'
I am a junior developer building an e-commerce web application using rails 4.2.4, Devise and a Pin scaffolding.
Right now, users can signup, sign in and then CREATE, READ UPDATE, DESTROY a pin on the index.html.erb.
Issue: I do not want users who sign in to be able to CRUD ( ONLY READ ).
I want to set this up so I as an ADMIN Can only create, update or destroy. Users and guests can only read.
I have been struggling with this for weeks, and would be grateful for help as to how I could achieve this please.
This is my PinsController
class PinsController < ApplicationController
before_action :set_pin, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
if params[:search].present? && !params[:search].nil?
#pins = Pin.where("description LIKE ?", "%#{params[:search]}%").paginate(:page => params[:page], :per_page => 15)
else
#pins = Pin.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 15)
end
end
def show
end
def new
#pin = current_user.pins.build
authorize(#pin)
end
def edit
end
def create
#pin = current_user.pins.build(pin_params)
if #pin.save
redirect_to #pin, notice: 'Pin was successfully created.'
else
render :new
end
end
def update
if #pin.update(pin_params)
redirect_to #pin, notice: 'Pin was successfully updated.'
else
render :edit
end
end
def destroy
#pin.destroy
redirect_to pins_url
end
private
# Use callbacks to share common setup or constraints between actions.
def set_pin
#pin = Pin.find_by(id: params[:id])
end
def correct_user
#pin = current_user.pins.find_by(id: params[:id])
redirect_to pins_path, notice: "Not authorized to edit this pin" if #pin.nil?
end
# Never trust parameters from the scary internet, only allow the white list through.
def pin_params
params.require(:pin).permit(:description, :image)
end
end
This is my Index.html.erb
<h1>For Sale</h1>
<%= form_tag pins_path, method: :get do %>
<div class="field">
<%= label_tag :Description %>
<%= text_field_tag :search %>
<%= submit_tag "Search", name: nil, class: "btn btn-success btn-sm" %>
<%= link_to 'Clear', pins_path, class: 'btn btn-danger btn-sm' %>
<% end %>
<div id="pins" class="transitions-enabled">
<% #pins.each do |pin| %>
<div class="box panel panel-default">
<%= link_to image_tag(pin.image.url(:medium)), pin %>
<div class="panel-body">
<%= pin.description %>
<%= link_to 'Show', pin_path(pin) %>
<% if current_user && pin.user == current_user %>
<%= link_to 'Edit', edit_pin_path(pin) %>
<%= link_to 'Destroy', pin, method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>
</div>
</div>
<% end %>
</div>
<div class="center">
<%= will_paginate #pins, renderer: BootstrapPagination::Rails %>
</div>
<div class=text-right>
<% if user_signed_in? %>
<%= link_to 'Post a Free Ad', new_pin_path, class: "btn btn-warning btn-lg" %>
</div>
<% end %>
<br>
You want everybody (logged in users or just quests) to be able to access the index and the show method. All other methods are exclusively for administrators.
First step: We need a way to identify an administrator. Add a boolean admin attribute to your the User model. Run the following command on your command line to create a new migration:
$ rails g migration add_admin_to_users admin:boolean
Open the generated file and add default: false to it, it should look like this:
class AddAdminToUsers < ActiveRecord::Migration
def change
add_column :users, :admin, :boolean, default: false
end
end
Now run rake db:migrate to add that column to your database.
Next step it to grant your own user administration rights. Log into the rails console (with $ rails c). Find your user and update the admin flag on your user to true:
> user = User.find_by(email: 'your-eamiladdress#example.tld')
> user.admin = true
> user.save
> user.admin?
# => true
As you see Rails automatically adds a admin? method to the user. We use that method in the controller now:
before_action :find_pin, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
before_action :ensure_admin, except: [:index, :show]
With this private methods:
def find_pin
#pin = Pin.find(params[:id]) # renders 404 in production when pin isn't found
end
def ensure_admin
unless current_user.admin?
redirect_to(pins_path, notice: 'Not authorized to edit this pin')
end
end
Use the same admin? method in the view to hide edit and destroy links from non-admins:
<%= link_to 'Show', pin_path(pin) %>
<% if current_user && current_user.admin? %>
<%= link_to 'Edit', edit_pin_path(pin) %>
<%= link_to 'Destroy', pin, method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>
so I'm trying to create a video game review website for practice.
A game has many reviews, and votes. The idea is, in order to post a review, you must vote "Good" or "Bad" first, THEN submit a review. You can't post a text review without voting.
I'm trying to do this without the acts_as_voteable gem...
The data format for votes is boolean. "Good" is true, "Bad" is false.
How do I get the votes to save? below are my routes.rb, _review partial, reviews controller, and show page.
many thanks guys :)
edit****: also I'm trying to only one vote per user. I was thinking of using a token variable which equals to 1, and when a vote is cast, the token is -1. Is that a good approach? But the data type for vote is boolean, so how would that work -- or should I change the data type for vote from boolean to integer?
edit#2 -- so I added :vote into my params.
routes.rb
upvote_game_review_path
POST /games/:game_id/reviews/:id/upvote(.:format) reviews#upvote
downvote_game_review_path
POST /games/:game_id/reviews/:id/downvote(.:format) reviews#downvote
Rails.application.routes.draw do
devise_for :users
root "games#index"
resources :games do
resources :news
resources :reviews, except: [:show, :index] do
member do
post "upvote"
post "downvote"
end
end
end
resources :platforms
resources :genres
end
reviews_controller.rb
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :update, :edit, :destroy]
before_action :set_game
before_action :authenticate_user!
def new
#review = Review.new
end
def create
#review = Review.new(review_params)
#review.user_id = current_user.id
#review.game_id = #game.id
if #review.save
redirect_to #game
else
render "review"
end
end
def upvote
#review.vote.create = true
redirect_to #game
end
def downvote
#review.vote.create
#review.vote = false
redirect_to #game
end
def edit
#review.update(review.params)
end
def destroy
#review.destroy
redirect_to #game
end
private
def set_review
#review = Review.find(params[:id])
end
def set_game
#game = Game.find(params[:game_id])
end
def review_params
params.require(:review).permit(:comment, :vote)
end
end
_review partial <-- to create a new review
<%= form_for [#game, #reviews.new] do |r| %>
<h3 class="post_review">Review this game</h3>
<p>
<%= r.text_area :comment %>
</p>
<p>
<%= button_to "Good", upvote_game_review_path(#game.id, r) %>
</p>
<p>
<%= button_to "Bad", downvote_game_review_path(#game.id, r) %>
</p>
<p>
<%= r.hidden_field :game_id, value: #game.id %>
<p>
<%= r.submit %>
<% end %>
show.html.erb
<p><%= link_to "<< Home", games_path %></p>
<span><%= link_to "Edit", edit_game_path(#game) %></span>
<span><%= link_to "Delete", game_path(#game), method: :delete %></span>
<div class="game_summary">
<h2><%= #game.title %></h2>
<%= image_tag #game.image %>
<p>Release Date: <%= #game.release_date %> </p>
<p>Genre: <%= #game.genre_id %> </p>
<p>Platforms: <%= #game.platform_id %></p>
</div>
<%= link_to "Add News", new_game_news_path(#game) %>
<h2>News & Articles</h2>
<%= link_to "view all", game_news_index_path(#game) %>
<% #news.each do |n| %>
<ol>
<li><%= link_to n.title, game_news_path(#game.id, n.id) %></li>
</ol>
<% end %>
<div class="game_review submit">
<%= render "review" %>
</div>
<% #reviews.each do |review| %>
<p><%= review.comment %></p>
<p><%= link_to "delete", game_review_path(#game.id, review.id), method: :delete %></p>
<% end %>
You don't specify which review you're loading in. The reason is here:
before_action :set_review, only: [:show, :update, :edit, :destroy]
You don't pull in the request's review instance when you go to either of those actions. Further, it doesn't look like you're actually saving them.
So, two things I'd recommend:
Add those methods to your before_action:
before_action :set_review, only: [:show, :update, :edit,
:destroy, :upvote, :downvote]
(May not be necessary, write tests to confirm this!) Actually save the entity after you've changed its value.
def upvote
#review.vote.create = true
#review.save
redirect_to #game
end
def downvote
#review.vote.create unless #review.vote
#review.vote = false
#review.save
redirect_to #game
end