Adding AJAX 'add to favorite' to this controller? - ruby-on-rails

This is blowing my mind, and there is so much going on that I just need to ask here for help.
Right now, I have a listing of resources. Inside each resource, it allows someone to 'add it as a favorite' by clicking a link. I have it working with normal redirection, but as for integrating ajax so they can favorite without the page refreshing, I am completely lost...
Right now I have it as a "put" action it seems for CRUD 'update'
FavoritesController (update is the only action in this controller)
def update
#favorite = Favorite.find_or_initialize_by_resource_id_and_user_id(params[:id], current_user.id)
if #favorite.persisted?
#favorite.destroy
else
if #favorite.valid?
#favorite.save
end
end
redirect_to root_url
end
My view:
<%= link_to "", favorites_path(:id => resource.id), :class => "star#{star_post?(resource)}", :method => "put" %>
My routes:
resource :favorites, :only => [:update]
My JS:
$('.res-list .star').click(function(){
$.put('/favorites/?id=' + $(this).attr('data-id'));
return false;
});

There's a couple of ways to do this. You can use link_to_function through which you can pass a javascript method (you can even pass a custom one if you've defined it in the view or in application.js). In this method, you can set up your AJAX call to hit your update action.
View (using jQuery's put macro):
<%= link_to_function "AJAX Link", "$.put('/favorites/#{resource.id}');" %>
Another way to do this is to give your link an addition HTML selector. Again, you would need to write a bit of js to hit your update action. I tend to like this way because I like to use buttons and what not instead of <a href="#"> tags. (Though honestly I ended up just creating a button_to_function method that calls content_tag(:button) instead of content_tag(:a))

I would recommend starting off with this Railscast on basic JQuery and Ajax processing. It's a bit dated, but is pretty solid still and will give you the basics to get you started.
http://railscasts.com/episodes/136-jquery
It will give you an idea of how to attach the ajax call to the element on your page, handle request processing and craft a basic javascript view partial that will update the form for the user.
Good luck!

Related

Rails 4 Link_ to Create New Record - Request Type

I am on Rails 4.
Just created a Favorite model & controller in which users can favorite articles. I have it set up so when a user clicks on the link, it routes to the favorite action inside of the favorites_controller. Right now, it is a get request, and inside of the action it is simply creating a new favorite.
My question is... is it ok that this is a get request? I feel like maybe a post request would be better (as forms use post to create things), but since there is no form in this case I decided a get request would be fine. Is this ok? or frowned upon?
here is the link_to in my view:
<%= link_to favorite_path(title: #article.title, id: #article.id) do %>
<span class="glyphicon glyphicon-star-empty"></span>
<% end %>
and the action it routes to in the controller:
def favorite
current_user.favorites.create(article_id: params[:id],
article_title: params[:title])
flash[:success] = "You added this page to your favorites."
redirect_to request.referrer || root_url
end
and my routes.rb:
get 'favorite' => 'favorites#favorite'
Everything works fine, just want some clarification as to the get request and if this is the best way to achieve what I am doing. If there is already documentation on this, please send it my way.
No, you shouldn't use a get request to create things or change states.
The reason is simple: Searchengines will follow links and trigger that action. Or some browsers may preload links to speed things up. In both case you will create records although the user never chose to click the link.
Instead just tell Rails to use some javascript to fake a post request (note the method: :post part):
<%= link_to(favorite_path(title: #article.title, id: #article.id),
method: :post) do %>
<span class="glyphicon glyphicon-star-empty"></span>
<% end %>
Read the doc about link_to for details.

How to prevent redirect after submitting data through a form

New to rails (programming as a whole), and I'm a little confused about routing.
I have a form in my view that takes someone's email, and then emails them once they hit submit. It works, but after I hit submit I'm being redirected to the view for the method I'm calling in order to mail the form. I don't want to be redirected, I want to stay on the same page. I have attempted using POST and PUT, but both redirect me. Am I using my controller incorrectly?
My controller has this method:
def mail
recipient_email = params[:email]
itinerary_body = params[:body]
x = ItineraryMailer.itinerary("#{recipient_email}", "#{itinerary_body}")
x.deliver
end
And here is the form from my view:
<%= form_tag({controller: "bookings", action: "mail"}, method: "post") do %>
<%= text_field_tag(:email) %>
<%= text_field_tag(:body) %>
<%= submit_tag("Email Me!") %>
My route:
match '/bookings', to: 'bookings#mail', via: 'post'
Thank you!
Since you don't have a redirect_to in your mail action Rails isn't redirecting you anywhere. Instead, it's just trying to render that action's view directly. However, I'm guessing that isn't the view you want rendered.
So, instead of trying to prevent a redirection (which isn't happening anyway) I would suggest that you take the opposite approach: use a redirection to get to the correct view. At the end of your mail action do this:
redirect_to action: :original_action_name
Where :original_action_name is the name of the action that gave you the view you want.
You could also use the render 'action' suggested in a comment, but that can cause problems in some cases where the current action may not have loaded or set up everything needed by the view you want to have rendered. Of course, you could just do that loading/setup in the current action but then you are doing the same thing in two places which isn't DRY.

link_to custom action but wrong method?

all, I'm trying to get a custom action to work with a put method: in the
in _post.html.erb i have a link_to statement:
<%= link_to 'End now', post, :method => :put, :action => endnow %>
routes.rb contains:
resources :posts do
member do
put :endnow
end
and posts_controller.rb looks like:
class PostsController < ApplicationController
helper_method :endnow
[.. code for create, edit, destroy, etc ..]
def endnow
puts params
end
end
rake routes's relevant line looks like:
endnow_post PUT /posts/:id/endnow(.:format) posts#endnow
However, the action endnow helper doesn't run when clicking on this link.
Strangely, it does run with an index action (which i can tell from the puts command.
Of course, eventually the code for endnow will update #post, but for now, it just doesn't run properly.
Maybe i'm going about this the wrong way - all I'm trying to achieve is to update #post upon clicking the link to that post, and before showing it.
Any ideas / Alternatives?
Why not use the route helper method provided to you? Change your link to
<%= link_to 'End now', endnow_post_path(#post), method: :put %>
Things you're doing wrong:
If you want to specify the :action, use the Symbol for the action (you're missing a colon). :action => endnow should be action: :endnow
I will assume you have a #post instance variable you're passing from your controller to your action. You should be using that instead of post (unless you do in fact have a local post variable you're omitting from your code)
You are using endnow as an action; you should remove the helper_method :endnow line in your controller because it's not something you want to/should be accessing from your view.
This can all be avoided by using the route helper (for endnow_post you'd append _path to get the local route path: endnow_post_path), and pass in your #post as an argument.
Because you're trying to do a PUT request, you must make sure you have something like jquery-ujs included in your asset pipeline to convert these links to form submissions behind the scenes; browsers don't support PUT via the click of a link on their own.
As for why you're getting the template error when you get your link_to working, Rails is telling you that you need to create a app/views/posts/endnow.html.erb file. Your action has only puts params which does not terminate execution, leaving Rails to assume you still are trying to render some endnow.html.erb template.
Are there other ways to do what you're trying to do (change a single attribute of a specific model)? Sure. Are there better ways? That's pretty subjective; it may not be the most RESTful way, but it's arguably easier to deal with (if for example there are very specific authorization rules to check before updating the attribute you are modifying in endnow. Does the way you've started fleshing out work? Absolutely.
Finally, as a bump in the right direction, after you fix your link_to and remove the the helper_method as I have described above, your endnow action might look like this:
def endnow
post = Post.find!(params[:id])
post.some_attribute_here = some_new_value_here
post.save
redirect_to :root and return # <- this line sets a redirect back to your homepage and terminates execution, telling rails to do the redirect and **not** to render some endnow.html.erb file
end

Add section to form by rendering a partial when link is clicked

UPDATE 3:
For anyone who reads this, this is why it wasn't working as expected in update 2 below: Passing a local variable to a partial that is rendered after the view has already loaded
If anyone knows how to solve that issue, let me know please.
UPDATE 2:
I updated the javascript with the quotation marks and it partially works...in the sense that the javascript is now functional and it will cause a string of text to appear on the page when I click the link as long as I have the partial only contain a string of text. However, when the partial includes the form fields code, something goes wrong.
If I just paste the following render code directly into the form in the new.html.erb view, it produces a new form section properly.
<%= render "add_round", f: f %>
However, when I try to include similar code in comps_helper.rb and then reference it from the link_to, it does not work:
In comps_helper.rb:
def addRound(f)
render "add_round", f: f
end
In new.html.erb:
<%= link_to "render it!", addRoundLink_path, remote: true %>
<div id="some_id"></div>
And I changed addRoundLink.js.erb to:
$("#some_id").html("<%=j addRound(f) %>"); #Is this the correct change to have made here?
Clicking the link_to link does nothing in that case.
Any thoughts?
UPDATED CODE:
Thanks for the reply. I've made the following changes and it still does not appear to be working. The link appears at the bottom of the form but when clicked does not change anything. What am I missing?
routes.rb:
resources :comps
match '/new_competition', :to => "comps#new"
get "/addRoundLink" => "comps#addRoundLink", :as => :addRoundLink
Note: I included the other 2 lines related to "comps" just in case those would cause an issue.
comps_controller.rb:
def addRoundLink
respond_to do |format|
format.js
end
end
comps_helper.rb:
def addRound
render "add_round"
end
addRoundLink.js.erb:
$("#some_id").html(<%=j addRound %>);
comps/new.html.erb:
<%= link_to "render it!", addRoundLink_path, remote: true %>
<div id="some_id"></div>
Thanks.
ORIGINAL QUESTION
First off, I'm new to rails. I've read and tried many solutions to similar questions but nothing has worked so far.
I created a form with rails form_for and fields_for. The form creates a new competition (comp). The competition has many rounds. The top half of the form (the form_for section) accepts the details about the competition as inputs and the bottom half of the form accepts details about each round (the fields_for section). The form works perfectly in this basic format.
I took all the code that is in the fields_for section and put it into a partial. My plan was to then create a "add new round" link to the bottom of the form that would simply display the partial above the link each time the link is pressed. This would add a new section to the form for a new round and allow the user to input as many rounds as they'd like. This is the part that I am struggling to make work.
I added this code to my comps_helper:
def addNewRound
render "add_round"
end
This renders the file /views/comps/_add_round.html.erb.
My question is: how do I get this to render in the form when a link is clicked. As far as I can get with the research I have done is:
<%= link_to "Add new round", { }, :remote => true %>
I don't exactly know what is supposed to go in the {} that will execute the addNewRound method. And I don't know what, if anything, I need to add to my comps_controller file.
Thanks so much for the help.
You have to create an action in your controller
app/controllers/some_controller.rb
def hello
respond_to do |format|
format.js
end
end
and define a route to this action.
routes.rb
get "/hello" => "some#hello", :as => :hello
then create a link to this action like that:
<%= link_to "render it!", hello_path, remote: true %>
<div id="some_id"></div>
When you click this link it will find its way to your action and respond with js(javascript) because we told action to respond with only js.
At the end render the partial to anywhere you want in your view(*in this example to the some_id div*)
app/views/some/hello.js.erb
$("#some_id").html("<%=j addNewRound %>");
WARNING: Creating dynamic forms is a pain. You will face a lot of problems (like setting different ids for new form elements etc...). I highly recommend you to use ryan bates nested_form gem

Rails 3: Simple AJAXy Page updates?

I can't believe I've been looking four hours for this simple task, but I have.
In Rails 2.3, I could replace one section of a page with this simple code:
render :update do |page|
page.replace_html "div_id", :partial => "new_content",...
end
In Rails 3, Ryan Bates has me writing entire new javascript functions, switching from Prototype (rails default) to jQuery, and otherwise not enjoying life. The other tutes are no more straightforward.
What am I missing? How do we replace a <div> these days?
Thanks, guys. The official answer seems to be that, yes, the team felt simple is the enemy of good and made it more complicated.
The first key is to create a .js.erb file NAMED for the method CALLING the ajax update. So if the index method handles the update, put the raw javascript in index.js.erb. This goes in the views folder.
Second, the code that worked in index.js.erb was
m = $('list_users');
m.innerHTML = "<%= escape_javascript(render :partial => "reload_users") %>";
Then to make the call, add in the respond_to block of the controller method, add:
format.js
Finally, the calling view has:
<%= link_to "Update User List", #reload_users_path, :remote => true %>
By the way, supposedly all the old pages using page.replace will work if you install a plugin. The plugin download page suggests that it broke in the last releases of Rails 3 and has not been fixed. Also, various bloggers will come to your home and birch-switch you if you use it.
The whole RJS stuff makes the javascript inline and makes the dom very obtrusive. Also, by avoiding inline javascript you could open up other possible ways of optimizing you javascript by compressing and caching those files in browser. Thats the reason why RJS is getting out of scope from rails 3. A little bit of getting around with jQuery or Prototype for a day should get you on gears with these kind of small stuff and will help the project on long run.
Do you still have jQuery in there? I'd recommend it over Prototype any day...
If it's still there you can just use the following in your Javascript:
$.get("<%= url_for path/to/partial %>",
function(response) {
$("#div_id").html(response);
});
This gets the partial via AJAX and just dumps it into the div with id div_id.
Hope this helps!
I'm not even sure you need to make an AJAX call to load that partial. I believe that in a js.erb file, a call to render(:partial => object_or_path) will just return a string with all the html, which you can wrap in a jQuery object and append. Example:
$('#div_id').html($('<%= render :partial => #object %>'))
As far as I know, along the same line as the answer above, you can do something like this in your template:
<%= link_to "Update User List", #reload_users_path, :remote => true %>
And in controller, do this:
respond_to do |format|
format.js {
render :text => "alert('reloaded')"
}
end
This way you can have controller "execute" client side JS much the same as as render :update used to do. This is equivalent to doing the following in Rails 2:
render :update do |page|
page << "alert('reloaded')"
end
Is there any reason why this approach is not advisable?
Try this:
page.call "$('#div_id').html", render(:partial => 'new_content')

Resources