I am trying to get some simple toggles to render small partials instead of changing the whole view - I'm not clear whether this is considered AJAX or not... anyway.
Here we have the user answering a question. While doing this, the question itself can be flagged as inappropriate.
View: answers#new.html.erb
Partial in view: _flag.html.erb
Controller for toggle: opinions/flag, which is supposed to update the opinion and render the flag partial inside the current answers#new.
# flag.html.erb
<% if #opinion.try(:flag) == true %>
<b>Flagged.</b>
<%= link_to "undo", opinions_flag_url(:id => #question.id, :flag => false), :remote => true %>
<% else %>
<%= link_to "flag this", opinions_flag_url(:id => #question.id, :flag => true), :remote => true %>
<% end %>
# last part of opinions controller / flag
if #opinion.save
format.html { render :partial => "answers/flag" }
format.xml { head :ok }
else
format.html { render :action => "new", :id=>params[:id] }
format.xml { render :xml => #opinion.errors, :status => :unprocessable_entity }
end
The partial is rendered, but not inside the current view (it's displayed on a blank page). In general, how do I render bits and pieces to the current view without Rails assuming it needs to show me the partial by itself?
Corollary: If I want to render text to a view, how do I specify where that new text will go?
You should use rjs. If you aren't sure if you are using it, you probably aren't :)
In short, you should use form_remote_for instead of form_for and you should use this in your controllers.
respond_to do |format|
...
format.js { Do stuff here }
end
Here's a screencast: http://railscasts.com/episodes/43-ajax-with-rjs
The specific command to put some html in a specific place would be:
page.replace_html "SOME_HTML_ID", "<p>SOME_HTML</p>"
But once you've seen the screencast, that should be obvious :)
Related
I have a vote button in my Rails app and to allow a user to vote I've created a route that connects the button and the respective action.
Route:
/posts/1/vote_up
But on other ROR websites I've been analyzing they can accomplish the same thing without creating a route (or at least, without showing it to the public user). An example would be Producthunt, there's a vote button, but when you hover over it, there is no route or URL mapped to it.
How does one do that? Can I link an action to a button without creating a route to it?
Without knowing much about your controller or models this is how you would do it. This is a silent Ajax call.
<%= form_tag(post, id: "post" + String(post.id),method: :patch, remote: true, authenticity_token: true) do %>
<%= hidden_field_tag "post[voter]", current_user.id %>
<%= hidden_field_tag "post[vote_up]", true %>
<%= button_tag "Up Vote", onclick: "$('#post#{String(post.id)}').trigger('submit.rails');" %>
<% end %>
The method: :patch will call your update method from your controller. So now you don't need to define a route.
If you're doing an increment in your DB you'll need to add the logic in your update method of your controller to catch the param, delete it, and then increase your count. You may also want to implement a way that the current user can only vote once per post.
In your controller you could add a JavaScript response of nothing if you'd like format.js {render nothing: true}
def update
respond_to do |format|
if #post.update(post_params)
format.js {render nothing: true}
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
else
format.js {render nothing: true}
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
In response to:
If I just need a 'link_to' to call a function in the controller with silent AJAX (instead of a form_tag doing that, like in your example), how would I achieve it?
A link by itself will not send any data... which is why you had to define a route. Unless you give the link params.
<%= link_to "Up Vote", post_path(#post, post: {up_vote: true}) %>
You can also provide a method for this. So if you put method: :patch it should call your update method.
<%= link_to "Up Vote", post_path(#post, post: {up_vote: true}), method: :patch %>
These examples will add a bunch of text in the url in the form of params:
# posts/1?post[up_vote]=true
So this works without configuring your routes.rb file. But you may need to add more params and modify your controller to handle these params.
My answer:
What I've written here will not appear as a form, but look just like a button. You can substitute button_tag with anything else that supports onclick to submit the data you want to the server.
Instead of a hyperlink reference you use hidden fields of a form to pass the data you want to use. You can't actually use link_to without giving it a hpyerlink reference. So a substitute for that is content_tag 'a'. Just replace the previous form button_tag line to produce this.
<%= form_tag(post, id: "post" + String(post.id),method: :patch, remote: true, authenticity_token: true) do %>
<%= hidden_field_tag "post[voter]", current_user.id %>
<%= hidden_field_tag "post[vote_up]", true %>
<%= content_tag 'a', "Up Vote", onclick: "$('#post#{String(post.id)}').trigger('submit.rails');" , style: 'cursor: pointer' %>
<% end %>
It now appears you have a regular link, and the mouse will change when you click on it. Now the issue is it will look like nothing happens when people click on the "Up Vote" link. You will need to add a CSS and/or JS action to change the user's view once they click on the link. So as well as the onlcick performing a submit you need to call a Javascript method to modify your content from "Up Vote" to "Thanks for Voting!". Other then that this works, it doesn't "appear" to be a form, and you have your link.
If you for some reason do want the page to reload after the link has been clicked you can change the JS response.
format.js { redirect_to #post, notice: 'Thanks for voting!' }
Try this. This is what we used to use before named routes:
link_to "Link to listing new", controller: :listings, action: :new
#or
link_to "string method", "/listings/new"
I have a web page with a couple of buttons and each button loads information on the page through ajax. Each button calls the same action in controller. That action, called "load" then calls an ajax file which in turns loads different information on the page depending on which button was clicked.
My buttons in my html file look like this.
<%= button_to "Load Profile", { :controller => "surveys", :action => "load"} %>
<%= button_to "Load Personality", { :controller => "surveys", :action => "load"} %>
<%= button_to "Load Experience", { :controller => "surveys", :action => "load"} %>
The load action in the surveys_controller.rb file looks like
def load
respond_to do |f|
f.js { render 'shared/ajax/info.js.erb' }
end
end
The info.js.erb file looks like this
$('#email').empty().html("<%= j render(:partial => 'shared/survey/tech/profile') %>")
This has worked for me in other places but now the content i need to load differs. I need the "#email" and the "shared/survey/tech/profile" to be a parameter sent from the load action. Can anyone help me?
Since you have the same action and same js.erb file for each button therefore you need to send some data using your buttons in order to differentiate with of the buttons is clicked.
For that you need to follow these steps:
a. Create a post request for your custom method load
post 'surveys/load' => "surveys#load"
b. Send some data through your buttons which will differentiate them:
<%= button_to "Load Profile", { :controller => "surveys", :action => "load", :profile => "yes"} , :method=>:post, :remote => true %>
# this will make you access params[:profile]
c. Check inside your load method which button is being clicked using params:
def load
#profile = params[:profile]
#other params to store values of personality and experience
respond_to do |f|
f.js { render 'shared/ajax/info.js.erb' }
end
end
d. Inside your info.js.erb file you could check which one of the variable is equal to yes and then render accordingly
<% if #profile == "yes" %>
#render your partial
<% elsif #personality == "yes" %>
#render your partial
<% elseif #experience == "yes" %>
#render your partial
<% end %>
On a second thought i think it'll be better if we just separate out this logic inside controller and depending on the params value render different js files like:
def load
#profile = params[:profile]
#other params to store values of personality and experience
if #profile == "yes"
respond_to do |f|
f.js { render 'shared/ajax/profile_info.js.erb' }
end
elsif #personality == "yes"
respond_to do |f|
f.js { render 'shared/ajax/personality_info.js.erb' }
end
end
end
Try something like this and see if it works for you;
<%= link_to "Load Profile", { :controller => "surveys", :action => "load"}, remote: true %>
<%= link_to "Load Personality", { :controller => "surveys", :action => "load"}, remote: true %>
<%= link_to "Load Experience", { :controller => "surveys", :action => "load"}, remote: true %>
respond_to do |form
format.js { }
end
and then;
$('#email').empty().html("<%= j render(:partial => 'shared/survey/tech/profile') %>")
I have a Rails app where on the home page home/index.html.erb, I show a partial for the model workorders. It currently defaults to the home_controleer.rb with the action index.
Can I select the workorders_controller.rb and action index2?
I tried this in the home/index.html.erb view file:
<%= render :partial => "workorders/index7", :controller => "workorders", :action => "index2" %>
But it's not working.
Try this instead:
<%= render :template => "workorders/index2" %>
source: https://stackoverflow.com/a/6051812/2128691
edit: in response to your last comment...
I haven't really worked with bootstrap, but maybe you could do something like this [pseudocode]:
def home
if [x condition]
redirect_to index_seven_path
else
render :action => [y action]
end
end
I am having a problem getting the page to update. I am completely lost and don't know where to start. My form for does have a :remote=>true The page initialy loads with a partial wrapped in a div tag called comments. I don't know what to place in the controller or what file to create, or even how to create it, to update the partial when the user clicks submit on the form. Thanks for any help.
Travis,
I'm not sure which action you're trying to perform here exactly, so let's assume you want to create a new resource.
Let's assume your resource is called Post
If you used rails to generate your PostController, you'll have a method create in there.
It may look something like this:
def create
#posts= Post.new(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to(#post, :notice => 'Post was successfully created.') }
format.xml { render :xml => #post, :status => :created, :location => #post}
else
format.html { render :action => "new" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
end
The key part here is that you'll need a format.js in the respond_to block.
When you put a :remote => true flag on your forms or links, you're telling rails that you want to make the request via Ajax. This results in a data-remote attribute being placed on your form or a element and that is in turn used to tell the unobtrusive Javascript to use Ajax to submit your request.
What you need to make sure is present on your end are the following:
In your controller, make sure there is a format.js response format listed in the respond_to block (see example above for html and xml.
Second, we're going to create a create.js.erb file under your app/views/posts folder. By default, rails will look for a action.format.erb file that corresponds to your action and format.
In the app/views/posts/create.js.erb file, you can now place your response javascript that will update your HTML document accordingly. If for example, you had a list of posts and you wanted to add a newly created one to the end of it, you may have something like this:
app/views/something/show.html.erb
<h1>Posts</h1>
<div id="posts">
<%= render #something.posts %>
</div>
<!-- here we will include the :remote => true option, which will add a data-remote attribute to our form -->
<%= form_for Post.new, :remote => true do |f| %>
<%= f.text_area :text %>
<%= f.submit %>
<% end %>
app/views/posts/create.js.erb
// here we're taking our newly created post and appending it to the list shown above
$('#posts').append("<%= escape_javascript(render #post) %>");
Finally, let's assume our post partial is something like this
app/views/posts/_post.html.erb
<p class="post-text"><%= #post.text %></p>
I'm not sure what javascript framework you're using, but here I'm using jQuery. I believe if you're using rails 3.1, jQuery is the default framework used, otherwise you'd have to look at jquery_ujs.
Hope this helps.
Have a post with comments. On post/show, when a user clicks the add comment button the server calls a javascript function that should add the new comment action as a partial:
render 'comments/new'
$("#newcomment").live("click",function() { $("#addcomment").load("<%= url_for :controller => 'comments', :action => 'new', :locals => {:parent_id => #post.parent_id} %>")
def new
#comment = Comment.new( :parent_id => params[:parent_id] )
render :partial => "form", :layout => false
end
new view:
render "form" # form is the add comment form
The problem is that the local variables are not passed and I can't add the comment (it wont call create)
render * never call an action in a controller, even render :action => 'edit'! It generates only the corresponding partial in every case. It may sometimes be confusing.
What you cand do to solve your probelm quickly is to call your partial via an ajax call.
In your views :
<div id="new_comment"></div>
In your controller :
render :update do
replace 'news_comment', partial => 'comments/new'
end
Hope this help.