unable to use `page.select` in render update inside controller - ruby-on-rails

In my controller I am using render update,but I want to update only if div element if present.
In order to check if div element is present, I am using page.select but its not responding with anything,neither error or output,here is my code
def post_update
render :update do |page|
page.replace_html('div_id':partial=>'my_partial') unless page.select("#div_id").blank?
end
end
is there anything I am missing here ?

You can't use ruby conditionals like that in rjs: that "unless" and page.select are executed on your server and have no idea what the contents of the DOM are.
I once wrote a blog post with a few ways of dealing with this. The short version is probably "generate Javascript fragments with your conditions". These days I'd stay away from rjs: render json or HTML and have some JavaScript on your page that knows what to do with it.

Related

Is it possible to cache a variable from a action after html.erb renders for css.erb? Rails

I have this action
class SearchesController < ApplicationController
...
def results
#users = User.search(params[:name], params[:gender])
render 'user_results'
end
...
The problem is rails is running back through this code again for when it renders my user_results.css.erb which is giving unsuspecting results as the params appear not to be available and I dont want to run back through the code again anyway.
I tried to cache the #users variable by doing #users ||= User.search(params... but it runs back through the code anyway and my search method which is giving different results than when it first runs through for the user_results.html.erb when the params are available.
How can I solve this?
Why are you running this code from user_results.css.erb at all? Is it necessary? Because this is a code smell to me. If you need to render custom CSS here then simply use a separate action, don't call it results call it user_results and the two calls will be separate.
The thing to remember is that there are actually 2 different web requests, one to the HTML and one to the CSS. They could be called individually in some cases, not necessarily tied together for all browsers, and that is why this is a code smell to me is you're blurring the lines between the two in your controller.
For more clarity, try adding this to your method:
logger.info "#{request.format}"
You could also enable/disable things based on the format:
if request.format.html?
If you are doing something like assigning a different color to each user in your CSS, then I would just move that into your HTML as an inline <style> block, and then you won't need any sort of caching. I'm 99% sure caching is not the right thing here.

Rails 3, remote form redirecting, want to stay on page, missing template error?

I think I must be doing something stupid. Unfortunately, due to the nature of stupidity, I can't tell what I'm being stupid about. So, as you can guess, I feel pretty stupid.
Please help me surmount this brick wall of stupidity.
I want to be able to click on a submit button and not leave the page, moreover, for that page to not even attempt to reload! This is accomplished by ajax and I've read several guides on it, I have even had it working on another project before. In this instance I'm hiding the div with scriptaculous. In the previous version of ajax form submission I used a (pseudo code)
render update do |page|
page replace html with this div
end
But this time I'm not actually going to replace the div or anything, I'm just hiding it with scriptaculous with an onclick event....
Alls I want is for the form to actually submit and for it to disappear.
So I'm working on a form, here's the form tag:
<%=form_for :call_back_request, :remote => true, :url => {:action => "call_back_request_cbrsent"} do |c| %>
As you can see it is :remote => true
I have various form entry fields, which I think are more or less irrelevant, here's the relevant parts:
This is a field that is part of the submit button, so I've included it here:
<%= hidden_field_tag :instructor_id, "empty", :id=>"instructor_hidden_field" %>
this is the submit button, as you can see I'm using scriptaculous to hide a div, that's the div the the submit button is in.
It could be affecting this so rather than delete that from this stackoverflow question I've included it.
<%=c.submit
:value=>"SEND",
:style=>"width:90px; height:33px; padding-bottom:0; font-size:1em;",
:class=>"lpl-go-button lpl-go-button-submit",
:onclick=>"Effect.BlindUp('cbr_row_#{rp[:name]}'); document.getElementById('instructor_id').value = '#{rp[:instructor_id]}';return false;", :remote=>true %>
(line breaks added by me here to prevent you guys having to scroll right so much)
From the controller :
def call_back_request_cbrsent
#call_back_request = CallBackRequest.new(params[:call_back_request])
#instructor = Instructor.find_by_id(params[:instructor_id])
#call_back_request.instructor_id = params[:instructor_id]
#call_back_request.save
email = UserMailer.create_send_call_back_request(#call_back_request, #instructor)
email.set_content_type("text/html")
UserMailer.deliver(email)
end
Now, the thing is, as far as my tired mind can see the return false; & the remote => true (in the submit tag specifically) do nothing much. My brain's cache is totally chock a block, I am having trouble thinking. Feel like I need to take a step back and stuff cause I'm not seeing the problems here.
Thanks to anyone who takes the effort to get through my ramblings here to give me an answer, I appreciate that I've got a lot of waffle up there. Really sorry. Dead tired. Think Dr. Jonestone said "sorry I didn't have time to write a short one" - bit of that going on here.
Just to clarify, currently I'm getting missing template error on a remote form... I tried adding this
render :nothing => true
But, it iss not working.
It does a good job at rendering a blank page, I don't know what the point in this would be so I am assuming that either it is not working properly for me or it's not appropriate in this context for some reason.
Try (in your controller)
render :nothing => true
Edit:
Ah, if you had a template error you should've said so.
I just noticed that you want to remove the div also.
So what you want to do, it in your controller:
def call_back_request_cbrsent
....
respond_to do |format|
format.html
format.js
end
end
And then make a call_back_request_cbrsent.js.erb file in your views. This will contain the javascript to remove that form. It obviously contain something like the following:
$("#form").remove()
...That's in jQuery, but just use whatever javascript you've been using to remove the form.

What's the best way to do UJS in rails when you have a re-usable widget?

In my current project I have a couple instances where I have a re-usable form that exists inside a rails partial. This form submits to a specific controller via ajax (:remote => true). The controller does some stuff and then returns back the appropriate js.erb to modify the page via javascript.
This works fine for when I have a single view. But the problem seems to happen when this re-usable partial exists on multiple views. In view 1 I might want to issue a completely different set of javascript commands then in view 2.
As a concrete example, say I have a comments controller that has the normal CRUD operations.
I now have partial called _comments_box.erb. This _comments_box.erb contains the ability to submit a comment via a simple line:
- form_for comment, :url => post_comments_path(post), :remote => true do |f|
This submits to a comments_controller.rb create method which looks somethings like this:
def create
... do some stuff, like create a new comments model
respond_to do |format|
# will respond with create.js.erb
format.js
end
end
The create.js.erb in turn adds a comment to the view, perhaps doing a bunch of other updates to the DOM.
Say I render the _comments_box.erb within a view called post_summary.erb. Now I have another view, post_detail.erb that requires the same _comments_box.erb. However the post_detail.erb requires me to update completely different divs on the DOM in response to a new comment.
I need to create a different JS response for each instantiation. So I can either:
Create an alternate controller method, say create_2. Pass in some parameter to the _comments_box.erb from post_detail.erb to the _comments_box.erb partial so it knows which controller method to fire. This will allow me to have a separate file _create_2.js.erb that will allow me to manipulate the post_detail.erb view independently.
Forget about using js.erb altogether and just use plain old AJAX and get back JSON, and handle the javascript manipulation completely on the client-side.
It seems option 1 allows me to continue to use the UJS supported by Rails which is nice. But also means I probably will be adding a lot of duplicate code everywhere which is annoying. Is there a way for me to do this elegantly while continuing to use UJS?
That's exactly the purpose of Apotomo: http://apotomo.de/
Here is it's own description:
Apotomo is a true MVC widget framework
for Rails. Widgets are based on Cells
and provide reuseable view components.
Having bubbling events, they know when
and how to update themselves via AJAX!
Working with Apotomo widgets almost
feels like developing GUI components –
in a Rails environment.
Have a try, it's great.
I'd not recommend using UJS for frontend apps: server shouldn't take care of client side business. I agree it's useful and clean but it lacks performance and thus should be kept for backend stuff (RJS will move into a gem, see here: http://weblog.rubyonrails.org/2011/4/21/jquery-new-default).
That said, back to the solutions you expose:
1) I think you won't need an extra controller, you'd just have to pass additional params in order to know from where to query came from. A hidden_field could do the trick. With this info, render the good js.erb file
format.js { if condition
render "create.js.erb"
else
render "create_2.js.erb"
end
}
2) I'd go for it and return json but you'll face the same problem: knowing from where the request comes from.
A better solution (than using a hidden_field) might be to check the request.referer in your controller action. This way you leverage the fact that each context has a unique URL, and don't have to explicitly specify another unique value when rendering your widget partial.

How to render a view normally after using render_to_string?

In my Rails application I have an action which creates a XML document using an XML Builder template (rxml) template and render_to_string. The XML document is forwarded to a backend server.
After creating the XML document I want to send a normal HTML response to the browser, but somehow Rails is remembering the first call to render_to_string.
For example:
Rails cannot find the default view show.html.erb because it looks for a show.rxml.
Simply putting a render 'mycontroller/show.html.erb' at the bottom of my action handler makes Rails find the template, but the browser doesn't work because the response header's content type is text/xml.
Is there any way to use render_to_string without "tainting" the actual browser response?
EDIT: It seems that in Rails 2 erase_render_results would do the trick, but in Rails 3 it is no longer available.
The pragmatic answer is that using a view file and two calls to render is Not The Rails Way: views are generally something that is sent to the client, and ActionPack is engineered to work that way.
That said, there's an easy way to achieve what you're trying to do. Rather than using ActionView, you could use Builder::XmlMarkup directly to generate your XML as a string:
def action_in_controller
buffer = ""
xml = Builder::XmlMarkup.new(buffer)
# build your XML - essentially copy your view.xml.builder file here
xml.element("value")
xml.element("value")
# send the contents of buffer to your 3rd server
# allow your controller to render your view normally
end
Have a look at the Builder documentation to see how it works.
The other feature of Builder that you can take advantage of is the fact that XML content is appended to the buffer using <<, so any IO stream can be used. Depending how you're sending content to the other server, you could wrap it all up quite nicely.
Of course, this could end up very messy and long, which is why you'd want to encapsulate this bit of functionality in another class, or as a method in your model.
Seems as if this may be a bug in rails 3 (at least compared to the behavior of 2.3.x render_to_string). In the source for 2.3.8 they clearly take extra steps to reset content_type and set the response body to nil (among other things).
def render_to_string
...
ensure
response.content_type = nil
erase_render_results
reset_variables_added_to_assigns
end
but in the 3.0.3 source for AbstractController::Rendering
def render_to_string(*args, &block)
options = _normalize_args(*args, &block)
_normalize_options(options)
render_to_body(options)
end
You can see there is no explicit resetting of variables, render_to_body just returns view_context.render. It is possible that content-type, response_body, etc are handled elsewhere and this is a red herring, but my first instinct would be to set
response.headers['Content-Type'] = 'text/html'
after your render_to_string before actually rendering.
In migrating the actionwebservice gem I encountered the same error. In their code they circumvent the double render exception by calling the function erase_render_results.
This function is no longer available in rails3. Luckily the fix is quite easy (but it took me a while to find).
Inside actionwebservice the following function was called inside a controller to allow a second render:
def reset_invocation_response
erase_render_results
response.instance_variable_set :#header, Rack::Utils::HeaderHash.new(::ActionController::Response::DEFAULT_HEADERS.merge("cookie" => []))
end
To make this work in rails3, you just have to write:
def reset_invocation_response
self.instance_variable_set(:#_response_body, nil)
response.instance_variable_set :#header, Rack::Utils::HeaderHash.new("cookie" => [], 'Content-Type' => 'text/html')
end
Hope this helps.

Rails Best Practices for RESTful controller CREATE and UPDATE methods

OK, I am trying to understand the best practices for the CREATE and UPDATE methods for both HTML and XML formats. The default code for a controller that the rails generator generates is a little unclear to me.
For the CREATE method, given a good save, the generator says to "redirect_to(#whatever)" for HTML and "render :xml => #whatever, :status => :created, :location => #whatever" for XML.
Given a bad save, the generator says to "render :action => 'new'" for HTML and "render :xml => #whatever.errors, :status => :unprocessable_entity" for XML.
However, for the UPDATE method, given a good update, the generator says to "redirect_to(#whatever)" for HTML and "head :ok" for XML.
And, given a bad update, the generator says to "render :action => 'edit'" for HTML and "render :xml => #whatever.errors, :status => :unprocessable_entity" for XML.
I understand this, and it makes sense to me, and works just fine - BUT, I have two questions:
First, for a successful CREATE and UPDATE, HTML format, why "redirect_to(#whatever)" instead of "render :action => 'show'"? I understand the differences between redirect and render, just more curious about which way you guys tend to do it and why. It seems the redirect would be an unnecessary extra trip for the browser to make.
Second, why "head :ok" upon successful UPDATE via XML, but "render :xml => #whatever, :status => :created, :location => #whatever" upon successful CREATE via XML? This seems inconsistent to me. Seems like a successful UPDATE via XML should be the same as a successful CREATE via XML. Seems like you would need the new/updated object returned so you could test it. How do you guys do it and why?
I'd already written this out when Sam C replied, but here it is anyway :-)
For the first part - why redirect instead of render? Two reasons I can think of:
1) It's consistent. If you rendered the show action and the user uses the back button later on return to that page, the user will see unexpected behaviour. Some versions of IE will give you some kind of session timeout error IIRC, other browsers may handle it a bit more gracefully.
Same goes for if the user bookmarked that page and returns to it at a later date using a GET request - they won't see the show action. Your app may throw an error or may render the index action because the user is requesting a URL like http://my.app.com/users, which would map to the index action when using a GET request.
2) if you render the show action without redirecting to a GET request and the user hits refresh, your browser will re-submit the POST request with all the same data, potentially creating duplicate instances of whatever it is you were creating. The browser will warn the user about this so they can abort, but it's potentially confusing and an unnecessary inconvenience.
As for the second part of your question, not too sure to be honest. My guess is that since you are already updating the object in question, you already have a copy of it so do not need another instance of it returned. Having said that, updating an object could trigger various callbacks which modify other attributes of the object, so returning that updated object with those modifications could make sense.
On create or update, redirect_to(#whatever) to clear the post, so that the user doesn't resubmit by refreshing. It also shows the correct url in the address bar for the create case, which posts to the collection path (/whatevers).
head :ok makes a minimal response on update, when usually you would already have the object in the dom. If you are updating the page after an update, the standard method is to use rjs views to update dom elements and render partials.

Resources