Use button_to to create habtm join? - ruby-on-rails

G'day all.
In a Rails app I have 2 models: users and spots, with a habtm relationship and join table. In the spot/show action I can create a form to ask the current user if they have visited this current spot (checkbox) and click save to create a record in the join table.
This works well (so I know my models and relationships are all good) however is not that elegant. Is there a way to do this without having to use a checkbox and submit button? Preferably with just a button?
My research suggests the rails button_to might do it, but I can't find a working example.
Many thanks.

Yes, button_to will work fine:
<%= button_to "I've visited here", {:action => "visited", :id => #spot} %>
Will generate a button that when pressed will pass in the #spot in the params as expected. You can then (assuming you have a current_user method because you're using a standard user model framework), do something like this:
def visited
spot = Spot.find(params[:id])
current_user.spots << spot
redirect_to :action => "show", :id => spot
end
Hope that helps.

Related

How to create an instance of a model upon button press

Let's say I have a User, Poll, and Vote model.
My user is created at registration and polls are created via a form the user has access to.
Upon showing a particular poll to a user, how can I create a new instance of the vote model when the user clicks a poll option?
Basically I want to create a new vote model instance when the user clicks a button that passes the current user_id and poll_id as parameters as well as a value for the option they selected.
My vote model 3 attributes: poll_id, user_id, and value.
poll_id and user_id are foreign keys to the poll and user tables, and value just records the chosen poll option.
I attempted to try and achieve this by calling Vote.create upon selecting the button like this:
<a class="btn-floating btn-large waves-effect waves-light hoverable red left" href="<%= poll_path(#next) %>" >
<i class="material-icons">arrow_back</i>
<%= Vote.create %>
</a>
This is how I'm getting access to the poll_id and user_id inside of my vote create method inside the vote controller
#current_user ||= User.find(session[:user_id]) if session[:user_id]
#vote.user_id = #current_user
#current_poll ||= Poll.find(session[:poll_id]) if session[:poll_id]
#vote.poll_id = #current_poll
Basically, I'm wondering what the correct rails-like way would be to go about this.
Disclaimer: I am very very new to rails and realise that I may be posing this question incorrectly!
You don't do Vote.create in the view. You do that in the controller.
Create a button using the appropriate helper in your .erb file, the vote_path, and passing in poll_id, user_id, and value. That will give you a button in your view that, when clicked, will POST to the create action on your VoteController where you will do the object instantiation and save. From there you do a redirect to where ever is appropriate given the results of the create action.
You'll need to go back and read up on how Rails uses ERB to construct HTML files for rendering in the browser. Your question suggests you haven't really groked it yet.
This solved my issue:
<td><%= button_to '<' ,{:controller => 'votes', :action => 'create', :user_id => 1, :poll_id => #poll.id, :value => 0}, {:method => :post} %></td>

Rails 4 - link back to form without reloading

Rails newbie here...please be kind...
I have an app that generates a unique paragraph of text (sampled from arrays and made unique based on criteria entered in _form.html.erb, rendered from the criteria/new page). On the criteria/show page, I have 2 links, one to generate a new unique paragraph using the same criteria, and one to return to the form and edit the criteria.
For the second link, I want to retain the criteria previously entered, as the user may want to only change one entry and not have to re-enter all of it. I've found plenty of information regarding ajax calls, respond_to, remote: true, but haven't found this exact answer or if I have, my brain is TIRED and failed to comprehend.
I've seen the suggestion in a similar question (How to come back to form without reset the values?) which talks about repopulating the data: "Just add the parameters (/myForm?param1=1&param2=2&param3=3) to the button url that leads back to the search form. Then populate the fields using the parameters. No need to use session variables." Sadly, I'm unclear about how to do implement this.
Would someone please be so kind as to walk me through either (preferably the simplest!) way of doing this?
Current links on show page (commented things are those that I tried unsuccessfully:
<%= link_to 'Generate Another!', criteria_path %> #regenerates text with current criteria
<%= link_to 'Enter New Information', new_criterium_path %> #this is the link I'm trying to change
<%#= link_to 'Enter New Information', new_criterium_path, :remote => true %>
<%#= link_to 'Enter New Information', { :controller => 'Criteria', :action => "new" } :remote => true %>
Controller new action (commented until I can make it work):
def new
#criterium = Criterium.new
#testing ajax stuff
# respond_to do |format|
# format.html
# format.json
# end
end
There's lots of talk about needing a new.js.erb file (or would that be show.js.erb?) and a div tag in my show(?) page but again, brain TIRED. I've attempted some of these things, then deleted from the code so as not to gak things up too much and be unable to remember what I did.
Please tell me what else you need from me in order to answer. Many thanks in advance!
Looks like you need to pass back params to your 'new' action, and then use them when instantiating Criterium:
<%= link_to 'Enter New Information', new_criterium_path(criteria: params[:criteria]) %>
def new
#criterium = Criterium.new(criteria: params[:criteria])
end

Using the nested_form gem, how do I create a valid link_to the "show" of a child object?

I use nested forms gem a bunch, but I haven't needed to navigate to the "show" page for a nested resource. Is there a way to do that simply, in the same style as the link_to_remove feature?
Associations are all set up and I have nested routes set up, but I can't figure out how to get an id into the "link_to" call.
Thanks for your help!
=f.fields_for :contacts do |cf|
.singleContact
=cf.label :"Email"
=cf.text_field :email
=cf.link_to "Show", client_contact_path(#client, cf.id)
=cf.link_to_remove "Delete Contact", :confirm => 'Are you sure you want to delete this comment?'
%hr
Try:
=link_to "Show", cf.object
Note that if you also use link_to_add in your view, then newly added children won't yet have an id until the form is submitted. So you may also want to add a condition that hides the "Show" link when cf.object.id is nil.

How do I create a button that increments an attribute of a model and can be pressed once per browser?

An apartment_listing has many reviews, and a review belongs to an apartment_listing.
In the file views/apartment_listings/show.html.erb, I show a list of reviews for that particular apartment_listing. These reviews are generated with the partial view apartment_listings/_review.html.erb like so:
<%= render :partial => "review", :collection => #apartment_listing.reviews %>
In _review, I want to have a button that, when pressed:
Increments that review's helpful_count attribute.
Makes it so that it cannot be pressed again while in the same browser - probably using cookies.
I feel like the former shouldn't be too hard to figure out, but it's got me beat. I'm really not sure where to start with the second goal.
EDIT: I managed to update the review's helpful_count attribute with this code in apartment_listings/_review.html.erb:
<%= form_for review, :method => :put, :remote => true do |f| %>
<%= f.hidden_field :helpful_count, value: (review.helpful_count + 1) % >
<%= f.submit 'Helpful?' %>
<% end %>
However, I'm not sure if this is the best way to do it, and I'd like to be able to disable the button after it is clicked.
Your code for updating helpful_count has the potential for problems. Imagine two users have loaded an apartment on their web page. One of them marks it helpful, and the next one does as well. Since when they initially loaded the page, helpful_count was the same, after both of them click helpful, the count will only be incremented by one: it would be updated twice to the same value.
Really, you want to create a new action, probably under the reviews resource for an apartment. That action could use ActiveRecord's increment method to update the helpful_count (technically there's still a race condition in increment!, you'd encounter it much less often) http://apidock.com/rails/ActiveRecord/Persistence/increment%21
Cookies seem like a reasonable solution for the latter problem. Simply bind to submit on the form with jQuery, and create the cookie in the handler.
What does the code look like in your reviews controller? More experienced RESTful coders might be able to speak more coherently on this, but the way I see it, incrementing the helpful_count attribute should be an action sent to the reviews controller. That way, you can create a link that performs the action asynchronously.
For example, inside _review.html.erb:
<% collection.each do |review| %>
<%= link_to "Mark as Helpful", "/apartment_listing/#{#apartment_listing.id}/reviews/#{#review.id}/incHelpful?nonce=#{SecureRandom.rand(16)}", :remote => true, :method => :put %>
# ... Do something cool with your review content ...
<% end %>
Inside your ReviewsController class:
def incHelpful
unless params[:nonce] == session[:nonce][params[:id]]
#review = Review.find(params[:id])
#review.helpful_count += 1
#review.update_attributes(:helpful_count)
session[:nonce][params[:id]] = params[:nonce]
end
render :nothing
# Optionally return some javascript or JSON back to the browser on success/error
end
Inside /config/routes.rb:
put "apartment_listing/:apart_id/reviews/:id/incHelpful" => "reviews#incHelpful"
The main idea here is that actions that edit a resource should use the PUT http method, and that change should be handled by that resource's controller. Rails' built-in AJAX functions are engaged by setting :remote => true inside the link_to helper. The second concept is that of a nonce, a random value that is only valid once. Once this value is set in the user's session, subsequent requests to incHelpful will do nothing.

Rails update PUT with same URL

I am calling a random Post and allowing users to +1 or -1 the post before loading another post. My model generates a random record at the URL /posts/random using the following.
Post.rb // Model
def self.find(*args)
if args.first.to_s == "random"
Post.find :first, :offset => rand(Post.count)
else
super
end
This code generates a random post when the user visits posts/random. However, I also defined a thumbs_up and thumbs_down field in the database that correspond to a thumb_up and a thumb_down image that when clicked, need to +1 or -1 the value in the database, before redirecting to another /posts/random. My understanding is that to do this I need to invoke the PUT update method, however, since my URL is posts/random instead of posts/1, how can I do this? Do I need to alter my routes?
First thing is that you'd need is an instance variable representing the random post so that you could use it in the thumbs up and thumbs down links. Then you need to make sure that those links look something like this:
<%= link_to "Thumbs Up", post_path(#post, thumbs_up: 1), method: :put %>
<%= link_to "Thumbs Down", post_path(#post, thumbs_down: 1), method: :put %>
That way, when in your update action for your posts controller, you can do something like this:
#post.update_attributes(thumbs_up: #post.thumbs_up + params[:thumbs_up],thumbs_down: #post.thumbs_down + params[:thumbs_down] )
That is only a start, but I think that should get you going in the right direction.

Resources