How do I attach an onclick event to a link_to_function such that clicking on the event refreshes an element on the page (using partials)?
When the user clicks the generated link, I'd like to refresh the partial containing the code so that i gets updated.
def add_step_link(form_builder)
logger.info 'ADD_STEP_LINK'
link_to_function 'add a step' do |page|
form_builder.fields_for :steps, Step.new, :child_index => 'NEW_RECORD' do |f|
logger.info 'inserted js'
html = render(:partial => 'step', :locals => { :step_form => f, :i=>#i+=1})
page << "$('steps').insert({ bottom: '#{escape_javascript(html)}'.replace(/NEW_RECORD/g, new Date().getTime()) });"
end
end
end
I have a partial in a larger form that contains just:
<%= add_step_link(technique_form) %>
I am attempting keeping track of the number of steps in a technique. In the form I am creating, users can add new steps to a set of instructions. Right now, I have default fields for steps 1-7. Adding one step, gets you step 8. The problem is that subsequent steps are numbered '8' also.
I am extending the "Multiple child models in a dynamic form" tutorial in http://railsforum.com/viewtopic.php?id=28447 for my own purposes.
Ok, I'm a little confused about what you are doing, but I'm going to try and make some suggestions.
Rather than use link_to_function, use link_to_remote. Link to function calls javascript, but what you actually want to do is call back to a controller that then runs some rjs to either replace the full partial or, more likely, append the new partial (containing the step) to the end of your current steps.
This is similar to all those examples you will have seen where people append a comment to the end of their blog comments (expect using link_to_remote rather than remote_form_for) see 3/4 of the way through http://media.rubyonrails.org/video/rails_blog_2.mov
You can see the docs for link_to_remote here: http://api.rubyonrails.org/classes/ActionView/Helpers/PrototypeHelper.html
I suggest using link_to_remote as RichH suggests. The trick for you it seems is keeping track of #i. I'd either put it as a URL parameter in link_to_remote, or in the session object. I'd suggest the session—it seems easier.
Related
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
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!
I've become a bit confused about the idea of "rendering" a "template" due to the way an author speaks about it in a book I'm reading.
My original understanding of "rendering a template" was that it meant that Rails is providing the content that is viewed on the screen/presented to the viewer (in the way that a partial is rendered) but the book I'm reading seems to be using the concept of "rendering a template" to also mean something else. Let me explain in context
This book (rails 3 in action) sets up a page layout using the conventional layouts/application.html.erb file, and then it "yields" to different view pages, such as views/tickets/show.html.erb which adds more content to the screen. that's all straightforward..
Within this view views/tickets/show.html.erb, there is a rendering of a partial (which is also a straightforward concept).
<div id='tags'><%= render #ticket.tags %></div>
Now within this partial there is, using ajax, a call to a "remove" method in the "tags_controller.rb" which is designed to allow authorized users to remove a "tag" from a "ticket" in our mock project management application.
<% if can?(:tag, #ticket.project) || current_user.admin? %>
<%= link_to "x", remove_ticket_tag_path(#ticket, tag),
:remote => true,
:method => :delete,
:html => { :id => "delete-#{tag.name.parameterize}" } %>
<% end %>
Now here is the "remove" action in the tags controller (which disassociates the tag from the ticket in the database)...
def remove
#ticket = Ticket.find(params[:ticket_id])
if can?(:tag, #ticket.project) || current_user.admin?
#tag = Tag.find(params[:id])
#ticket.tags -= [#tag]
#ticket.save
end
end
end
At the end of this remove action, the author originally included render :nothing => true , but then he revised the action because, as he says, "you’re going to get it to render a template." Here's where I get confused
The template that he gets this action to render is "remove.js.erb", which only has one line of jquery inside it, whose purpose is to remove the "tag" from the page (i.e. the tag that the user sees on the screen) now that it has been disassociated from the ticket in the database.
$('#tag-<%= #tag.name.parameterize %>').remove();
When I read "rendering a template" I expect the application to be inserting content into the page, but the template rendered by the "remove" action in the controller only calls a jquery function that removes one element from the page.
If a "template" is "rendered", I'm expecting another template to be removed (in order to make room for the new template), or I'm expecting content to be "rendered" in the way that a partial is rendered. Can you clarify what is actually happening when a "template" is "rendered" in the situation with the jquery in this question? Is it actually putting a new page in front of the user (I expected some sort of physical page to be rendered)
You're nearly there! Rendering a template is indeed always about producing content, but for a slightly wider description of content. It could be a chunk of html, for example an ajax call to get new items might produce some html describing the new items, but it doesn't have to be.
A template might produce javascript as it does in your second example. Personally I am trying to avoid this and instead pass JSON back to the client and let the client side js perform the required work.
Another type of rendering you might perform is to produce some JSON. APIs will often do this, but you might also do this on a normal page. For example rather than rendering some javascript to delete tag x you might render the json
{ to_delete: "tag-123"}
and then have your jQuery success callback use that payload to know which element to remove from the DOM, by having this in your application.js file
$('a.delete_tag').live('ajax:success', function(data){
var selector = '#' + data.to_delete;
$(selector).remove()
}
(Assuming that your delete links had the class 'delete_tag')
Rendering JSON like this isn't really a template at all, since you'd usually do this via
render :json => {:to_delete => "tag-#{#tag.name.parameterize}"}
although I suppose you could use an erb template for this (I can't imagine why though).
My understanding is that js.erb is "rendered" by executing the javascript functions within it. Very often something like the below is done:
jQuery(document).ready(function() {
jQuery('#element').html('<%= escape_javascript(render pages/content) %>');
});
There's a really succinct overview of rendering at http://guides.rubyonrails.org/layouts_and_rendering.html that may help as it also goes into the details of the ActionController::Base#render method and what happens behind the scenes when you use render :nothing (for example). Render but can be used for files or inline code as well -- not just 'templates' in the traditional sense.
I'm using periodically_call_remote to update a portion of a page that contains a list of objects. I send along with the url a param containing the created_at date for the most recent object in the database. The action that is called then get all the objects that have been created since then and renders a partial which displays them at the top of the list.
The problem is that I can't seem to figure out how to make it so that the next time periodically_call_remote triggers it sends along the created_at date for the new most recent object (if there is one). I tried putting the periodically_call_remote inside the partial that is being rendered but that caused all sorts of problems (This explains why you shouldn't do that).
Is there some way I can make periodically_call_remote send along a new param each time it's called? As it stands right now it just sends the same one over and over which means that new objects get rendered more than once.
Hackery you say? :)
Store the parameter in a hidden div. You can access the contents of that div via Javascript, which periodically_call_remote will accept as part of the :with option.
When the parameter changes, simply update the contents of the hidden div as part of your controller action.
So, for example..
<div id="date_to_check_from" style="display:none;"><%= #initial_created_at %></div>
<%= periodically_call_remote :url => path_to_controller(#normal_params), :with => "'date_to_check_from=' + $('#date_to_check_from').html()", :method => :get, :frequency => 10 %>
That will get you params[:date_to_check_from] in your controller. Then update however you want, e.g.,
render :update do |page|
page << "$('#date_to_check_from').html('#{#new_date_to_check_from}');
end
It's possible to do some hackery, but I recommend that you start looking into writing your own JavaScript code to perform AJAX requests. Rails' helpers like periodically_call_remote aren't meant to be used in such complex situations.
I have a model called Details, and two controller methods new and create. New method displays a form to create Details (in new.html.erb), which gets posted to the Create method that saves it. (when it succesffully saves, it it renders the new.html.erb file with the details.) This works as expected.
Now i want a separate page with a link to fill in these details. I want the click to do the intended work through a popup, example redbox. When you click on that link, a popup should show the form, whose submit should post the form, and if it is successfully done, then refresh the original page. If the post is unsaved, then the same form should show the errors. What do i need to do to make it work in Ror? I guess i need some stuff to go in new.js.rjs and maybe create.js.rjs, but i can't really figure out what to put in those files.
Redbox updates the page's dom to add a div at the end of it. So your form is a part of the main page.
So if you add a form tag in this redbox, all your page will be reloaded as there's only one.
So you add anywhere in your page (preferably at the end) :
<div id="redbox" style="display: none;">
<%= form_for #details do %>
# Whatever form fields you want here
<% end -%>
</div>
You do a link that'll open the redbox :
<%= link_to_redbox 'Open The Details Form', 'redbox' %>
This will display your redbox with your form.
And as it is the same page, not a new windows, when you'll validate your form, it'll reload the all of it.
I used a link_to_remote_redbox for this, which on clicking, fetches the form rendered by the new method call in a popup widnow. it submits to the create function and it is now working. actually i had previously made it work without ajax and i was having difficulty in making it work with ajax. the problem was solved once i made separate partials for ajax calls, and doing:
respond_to do |format|
format.js { render :template => 'detail/ajax_template'}
...
end
I provided different templates for both create and new method calls, used the :html => {:id => 'some-id'} in form and the :update => {:some-id} to replace the form with the message that it worked correctly.
So I didnt know that i needed separate templates for the different styles of forms, and that i needed to use the :update option to replace the form when i asked the above question.