Toggling a boolean with link_to - ruby-on-rails

I've read through the relevant Stack questions but still seem to be hitting a routing error with the following code:
Routes.rb
resources :memberships do
put :toggleon
put :toggleoff
end
Memberships_controller.rb
def toggleon
#membership = Membership.find(params[:id])
#membership.update_attributes(:active => true)
if user.id == membership_id
redirect_to root_path
else
redirect_to group
end
end
def toggleoff
#membership = Membership.find(params[:id])
#membership.update_attributes(:active => false)
if user.id == membership_id
redirect_to root_path
else
redirect_to group
end
end
Show.html.erb
<% if this_membership.active %>
<%= link_to 'Pause', this_membership, controller: :memberships, method: :toggleoff, style: 'color:#ccc' %>
<% else %>
<%= link_to 'Start', this_membership, controller: :memberships, method: :toggleon, style: 'color:green' %>
<% end %>
Error text
Started POST "/memberships/13" for 127.0.0.1 at 2011-09-27 23:35:35 +0100
ActionController::RoutingError (No route matches "/memberships/13"):
I can destroy memberships just fine but toggling the membership activity is proving tricky.
Thanks in advance!
Second attempt
<%= link_to("Pause", membership_toggle(#membership), :method => :put, :title => "This toggles it off") %>
<% else %>
<%= link_to("Toggle On", membership_toggle(#membership), :method => :put, :title => "This toggles it on") %>
and
def toggle
#membership = Membership.find(params[:id])
#membership.toggle!(active)
end
and
resources :memberships do
member do
put :toggle
end
end
now gives me this error...
undefined method `membership_toggle' for #<#<Class:0x00000102e69bc0>:0x00000102e66ec0>

Links using your routes are going to be:
link_to("Toggle On", membership_toggleon_url(membership), :method => :put, :title => "This toggles it on")
link_to("Toggle Off", membership_toggleoff_url(membership), :method => :put, :title => "This toggles it off")
You need to supply :method => :put to the link_to method given that's what you have the route as in your routes file.
Something like this can be achieved with a single action:
resources :memberships do
member do
put :toggle
end
end
Then you can make use of a single boolean field in your controller by using the Rails toggle method.
This will let you do something like:
#membership.toggle(:on)

Try setting the route as being :on => :member.
Also, there's an atomic toggle! method that does what its name implies.

Kept having issues with various techniques but eventually found this article:
http://buckybits.blogspot.com/2011/09/simple-ajax-property-toggle-in-rails-30.html
BOOM!

Related

link_to redirects me to index action and not to the :action one

In my redmine plugin view, I've got this link:
<%= link_to "Add", :controller => "important_user", :action => "u_edit", :u_id => user.id, :p_id => #project.id, :method => :post %>
routes.rb:
resources :important_user do
collection do
post :u_edit
end
end
and controller:
class ImportantUserController < ApplicationController
def u_edit
puts 'edit!'
end
def index
puts 'ciao'
puts params[:p_id]
puts params[:u_id]
end
end
In spite of calling the expected u_edit action, clicking on the link calls the index method (I created in a second moment to avoid the AbstractController::ActionNotFound (The action 'index' could not be found for ImportantUserController) error). I've also tried using this sort of link:
<%= link_to 'Add', { :action => 'create', :u_id => user.id, :p_id => #project.id}, :method => :post %>
But it didn't work either, returning a projects?p_id=1&u_id=1 GET 404. How could I make it call the desired u_edit action?

Rails form drop down select data from database pulled from a different table

I have three models that are connected like the following
Users
has_many :training_resources, through: :user_training_resources
has_many :user_training_resources, dependent: :destroy
TrainingResources
has_many :user_training_resource, dependent: :destroy
has_many :users, through: :user_training_resource
UserTrainingResources
belongs_to :user
belongs_to :training_resource
I'm trying to build out a form on my TrainingResource show page to allow the user to change the status of a UserTrainingResource and it's failed pretty miserably.
This is the latest attempt:
<% #training_resource.spud_users.each do |training| %>
<tr>
<td><%= training.full_name %></td>
<% utr = training.user_training_resources.where(training_resource: #training_resource).first %>
<td>
<%= tb_form_for [:admin, #training_resource], :remote => true, :data => {:errors => :inline, :success => admin_training_resources_path} do |f| %>
<%= f.collection_select :user_training_resource_ids, #training_resource, :id, :name %>
<%= f.tb_save_buttons('', admin_training_resources_path) %>
<% end %>
</td>
<td class="table-actions">
<%= link_to 'Delete', admin_user_training_resource_path(training), :method => :delete, :data => {:confirm => 'Are you sure you want to delete this?'}, :class => 'btn btn-danger btn-sm' %>
</td>
</tr>
<% end %>
The delete works fine but the select at the moment errors out with: undefined method 'map'.
I've also tried:
<%= f.select(:user_training_resource_ids, UserTrainingResource.pluck(:status, :id))%>
No error on the page but the options displayed appear to display the current status for ALL UserTrainingResource. Plus it's not actually saving a change.
I've tried:
<%= f.select(:user_training_resource_ids, options_for_select(['pending', 'confirmed', 'rejected'], selected: utr.status))%>
This actually does present the options because I'm being explicit and it's actually showing the current status. However it's not saving when a selection is made.
Is this just a simple issue of a wrong route? Is there a better way to select data from a different table in a rails drop down?
Edit:
Here's the UserTrainingResources Controller. It's pointing to the TrainingResources Controller's Admin:
class Admin::UserTrainingResourcesController < Admin::ApplicationController
belongs_to_app :training_resources
add_breadcrumb 'Training Resources', :admin_training_resources_path
before_action :load_training_resource, only: [:show, :edit, :update, :destroy]
respond_to :html, :json
def new
#user_training_resource = UserTrainingResource.new
respond_with #user_training_resource
end
def edit
respond_with #user_training_resource
end
def show
respond_with #user_training_resource
end
def update
flash[:notice] = 'UserTrainingResource updated successfully' if #user_training_resource.update(user_training_resource_params)
respond_with #user_training_resource, location: admin_training_resources_path
end
def destroy
flash[:notice] = 'UserTrainingResource deleted successfully' if #user_training_resource.destroy
respond_with #user_training_resource, location: admin_training_resources_path
end
private
def load_training_resource
#user_training_resource = UserTrainingResource.find_by!(id: params[:id])
end
def user_training_resource_params
params.require(:user_training_resources).permit(
:training_resources_id, :status).merge(user_id: current_user_id)
end
end
Another edit: Routes
I feel like this is an issue with saving to the correct place so I've included below the routes for user_training_resources:
get '/user_training_resources' => 'user_training_resources#index'
delete '/user_training_resources/:id' => 'user_training_resources#destroy', as: :destroy_utr
put '/user_training_resources/:id' => 'user_training_resources#update', as: :update_utr
The issue is actually the form_for being passed to the wrong url. Changed it to reflect a new path url: admin_update_utr_path(utr)
It may well be the route - the route for the form using the POST or PATCH (not sure which) method while your route is expecting a PUT method.
To confirm, check your console to see if the route is resolving to the correct controller action. If not, you can either change the routes to be like:
post '/user_training_resources/:id' => 'user_training_resources#update', as: :update_utr
or
patch '/user_training_resources/:id' => 'user_training_resources#update', as: :update_utr
I think you need to set "multiple" because #training_resource can has many "user_training_resource_ids"
try this :
<%= f.collection_select :user_training_resource_ids, current_user.user_training_resources,
:id, :name,
{:prompt => "Please select training resources"},
{:multiple => true} %>
source : https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_select

In Rails, how do you call a specific method in a controller from a form in a view?

new to rails so bear with me. What I would like to do, is I have a list of messages on the screen. I want the message to be marked as 'closed' which means it just fills in a completeDate.
I have a model:
class Message < ActiveRecord::Base
attr_accessible :topic, :body, :completeDate, :created_at
belongs_to :account
belongs_to :user
has_many :message_followups
end
I have a controller:
class MessagesController < ApplicationController
def close
#message = Message.find(params[:messageId])
#message.completeDate = Date.today
if #message.save
redirect_to myHomeMessages_path, :flash => { :success => "Your message was closed." }
else
redirect_to myHomeMessages_path, :flash => { :error => "Error closing message." }
end
end
end
I have a view:
<%= form_for (???) do |f| %>
Are you sure you wish to close this message?<br>
<%= hidden_field_tag 'messageId', message.id.to_s %>
<%= submit_tag "Close Message" %>
<% end %>
I'm having a problem figuring out how to get the form_for or a form_tag to call the specific method 'close' in the messages controller. Any help would be greatly appreciated, thanks.
Matt
A very important hint, you should not use camel case for variable names on Ruby, variable sand methods should use underscore as separators, if you're coming from another language like Java, try to avoid using the same naming patterns you used in there. Camel case in ruby is only for class and module names.
I am guessing you're using Ruby on Rails 3, as you haven't said what you're using. First, you need a route for that, it would look like this on your routes.rb file:
resources :messages do
member do
post 'close'
end
end
A little change to your controller
def close
#message = Message.find(params[:id]) #you don't need to use the hidden field here
#message.completeDate = Date.today
if #message.save
redirect_to myHomeMessages_path, :flash => { :success => "Your message was closed." }
else
redirect_to myHomeMessages_path, :flash => { :error => "Error closing message." }
end
end
And your form is going to look like this:
<%= form_for( #message, :url => close_message_path( #message ), :html => { :method => :post } ) do |f| %>
Are you sure you wish to close this message?<br>
<%= f.submit "Close Message" %>
<% end %>
This form is going to be posted to "/messages/ID_HERE/close" and Rails is going to set the "ID_HERE" value as the "id" parameter on your request.
You can see the full documentation about form_for here.
Something like this should square things away for you:
<%= form_for #message, :url => { :action => "close" }, :html => { :method => :put } do |f| %>
What this does is specify that you are performing an HTTP PUT method to the close method in the messages controller. For more information about the form_for helper method read here
Do you have an update method in your controller, if not you can just do this:
<%= form_for( #message ) do |f|
<p>Are you sure you wish to close this message?</p>
<%= hidden_field_tag :close %>
<%= f.submit "Close Message" %>
<% end %>
Then in your controller:
class MessagesController < ApplicationController
def update
#message = Message.find(params[:id])
#message.completeDate = Date.today if params.has_key?(:close)
if #message.save
redirect_to myHomeMessages_path,
:flash => { :success => "Your message was closed." }
else
redirect_to myHomeMessages_path,
:flash => { :error => "Error closing message." }
end
end
end

Posting to controller via link_to

I am trying to send a :vote parameter of 'up' to my controller, so that it performs the voting function of current_user.vote_exclusively_for(#book). I am using the thumbs up gem.
I am trying to do this using link_to, and the correct parameters are showing up in my server output, but it is not working with the controller. I must be doing something wrong, but I am not sure what. Do i need to do something different with routes, other than books :resources?
This my vote action in books_controller
def vote
#book = Book.find(params[:id])
if params[:vote] == 'up'
current_user.vote_exclusively_for(#book)
end
redirect_to #book
end
And this is the link_to example in my view:
<%= link_to "Vote Up", :url => { :controller => "books", :action => "vote", :vote => "up"}, :method => :post %>
Any advice on where my attempts are breaking down would be greatly appreciated ( extra note: when i put the current_user.vote_exclusively_for(#book) function in my view it works) so I think this is a view/routes/link_to issue, not the function itself.
I don't understand your link_to. It seems to be missing the ID of the book it's voting on?
Make sure your routes.rb file looks like this:
resources :books do
post :vote, :on => :member
end
Then change your link_to function to this:
link_to "Vote Up", vote_book_path(#book, :vote => "up"), :method => :post
I just had a similar problem and solved it by using this style syntax:
<%= link_to "Vote Up", {:controller => "books", :action => :vote, :vote => "up" }, {:method => :post} %>
Also make sure your routes.rb has something similar to
resources books do
post :vote
end

Routing is using ID has action and action has ID after submitting a form

I have a model User that has_one user_profile and a User_Profile belongs_to user
in the User controller I have:
def personal
#user = User.find_by_id(params[:id])
#user_profile = #user.user_profile
#user_profile ||= #user.build_user_profile
end
def update_personal
#user = User.find_by_id(params[:id])
if #user.user_profile.update_attributes(params[:user_profile])
flash[:notice] = "OK"
redirect_to #user
else
flash[:notice] = "Fail"
render :action => 'update_personal'
end
end
In my personal.html.erb view I have:
<% semantic_form_for #user_profile, :url => { :action => "update_personal"} do |form| %>
<%= form.inputs %>
<%= form.buttons %>
<%end%>
And on the rountes I have:
map.resources :users, :member => {
:personal => :get,
:update_personal => :put
}
Now the strange thing is that I can do:
users/1/personal
to see the form but when I submit I get this error:
Unknown action
No action responded to 1.
It's trying to find an action with the name 1.
Can anyone point me out on the right direction?
I just got the problem again, and finally understood the problem and found the solution.
This time i was using an ajax call using getJason.
Because the call was a get, and in my routes I had a update_xxxxxx => :put,
the route was ignored and the default :controller/:action/:id was used.
I just had to put the update_xxxx => :get and the problem was solved.
Maybe this will help someone.

Resources