How can I use different RJS templates from the same rails controller? - ruby-on-rails

I have a controller method that returns a list for a drop down that gets rendered in a partial, but depending on where the partial is being used, the RJS template needs to be different. Can I pass a parameter to the controller that will determine which RJS gets used?
Here is the controller method, it is very simple:
def services
respond_to do |format|
format.js {
#type = HospitalCriteria.find_by_id(params[:type_id])
#services = #type.children.all
}
end
end
And here is the rjs template the gets rendered automatically
page.replace_html 'select_service', :partial => 'hospital/services'
page.replace_html 'select_condition', :partial => 'hospital/conditions'
page.replace_html 'select_procedure', :partial => 'hospital/procedures'
page << 'if ($("chosenType") != null) {'
page.replace_html 'chosenType', #type.name
page.replace_html 'chosenService', 'Selected Service'
page.replace_html 'chosenCondition', 'Selected Condition'
page.replace_html 'chosenProcedure', 'Selected Procedure'
page << '}'

I like Mike's response, but something to think about here from a design perspective:
It sounds to me like this should be in the view layer - if the action is semantically the same, but the presentation is different, perhaps having two different rjs partials and doing something like below is more compliant with MVC?
if params[:use_alternate]
render :partial => "case_1.rjs"
else
render :partial => "case_2.rjs"
end

What about placing the conditional logic in one rjs template?
# services.rjs
if #type == "your conditions"
# your rjs updates
else
# your other rjs updates
end
This gives you a cleaner controller and saves you the headache of maintaining multiple rjs templates.

something like:
if params[:use_alternate]
render :template => alternate.rjs and return
end

To keep things clean, I'd have two controller methods that render the two different RJSs. I'd then set #type and #services in a common protected method that the two controller methods call.
In my mind you are asking for something different in each case so call a different controller method. Passing in a flag to change the way the method works is just a hack and won't scale well when you have 3, 4 or 5 places. Even though you'll generate more code, it will be easier to maintain.

Related

Rails 2 Render ERb template in controller?

This code in the controller
av = ActionView::Base.new(Rails::Configuration.new.view_path)
av.extend ApplicationHelper
content = av.render(:file => "show", :locals => { :user => #user })
and the show.html.erb have the link_to helper , operation code error
undefined method `url_for' for nil:NilClass
I add av.extend ActionController::UrlWriter in the controller , still error
undefined method `default_url_options' for ActionView::Base:Class
Try:
content = render_to_string(:file => "show", :locals => { :user => #user })
Usually, in Rails, when something is very hard, it is because you are not approaching the problem from an ideal angle. I can't really answer this question directly, other than to advise not to do this. Ideally, view logic should be in the view, and not the controller. With a few rare exceptions, like using a link_to helper in a flash message (which can be easily solved), these concerns should be separated. This does not seem like one of those exceptions. Instead, I would recommend one of the following (slightly more Rails-y) techniques:
Option 1:
It looks like you are trying to render the view for the show action. This can easily be accomplished by using render :action => 'show' (docs). This will not run the code for the action, just use that view.
Option 2
In the event that option 1 is not viable in your situation, you may alternatively consider some variation of the following technique. Render the default view, as normal. Move the existing content of the view into a partial, and your new content into a partial of its own. Then in your view, simply toggle the partial to render based off of an appropriate condition - the existence of #user, for this example: render :partial => #user ? 'new_content' : 'existing_content'. Depending on your application structure, it may be that this can be further simplified by just rendering the same partial from your show view and the view for the action referenced in the question.
I think keeping the various elements of an application isolated into their intended concerns not only makes this easier to develop and maintain by following the principle of least astonishment, but also usually makes the application much easier to test, as well. Sorry for not answering your question - hope this helps, anyway.
I suppose it was called outside controller so I do it this way in Rails 3:
av = ActionView::Base.new(Rails.configuration.paths["app/views"])
av.class_eval do
include ApplicationHelper
include Rails.application.routes.url_helpers
default_url_options[:host] = 'yoursite.com'
def protect_against_forgery?
false
end
end
#result = av.render(:file => "show", :locals => { :user => #user })

Specify namespace when using "render" with an object in rails 3

So, you can do something like that in rails:
#features.each do |feature|
render feature
end
and it will look for a partial called _feature.html.erb in the views/features folder, based on the class name.
But what if features are in a cms namespace?
Is it possible to specify the namespace? Doing something like this (it doesnt work, obviously)
render [:cms, feature]
Thx
You'll have to be more explicit:
render :partial => '/cms/feature', :object => feature
This will render the 'app/views/cms/_feature.html.erb' partial with the object being 'feature'.

Rails 3, rendering a partial for the given controller if it exists?

In my application.html.erb layout for my app, I want to have a partial that renders if it exists for the given view. for example.
If the visitor is at http://example.com/users/show, I'd want the partial /users/_sidebar.html.erb to render.
But if the visitor were at say, http://example.com/user/locations/san_francisco, I'd want the partial /users/locations/_sidebar.html.erb to render.
So the thing here is that if there were no partial for that controller/action it would render some generic partial in my shared directory, and I'd rather not litter every single view with content_for blocks ya know?
Any ideas guys?
My solution is a bit different. Throw this in your application helper:
def render_partial_if_exists(base_name, options={})
file_name = ::Rails.root.to_s+"/app/views/layouts/_#{base_name}.html.erb"
partial_name = "layouts/#{base_name}"
else_file_name = ::Rails.root.to_s+"/app/views/layouts/_#{options[:else]}.html.erb"
else_partial_name = "layouts/#{options[:else]}"
if File.exists?(file_name)
render :partial => partial_name
elsif (options.key?(:else) and !options[:else].nil? and File.exists?(else_file_name))
render :partial => else_partial_name
end
end
Then in your view:
<%= render_partial_if_exists "page_#{controller.action_name}_sidebar", :else => "page_sidebar" %>
In an edit action, if "layouts/page_edit_sidebar" exists it renders it, otherwise it will render a standby "layouts/page_sidebar"
Sean Behan has a great post on exactly this:
http://seanbehan.com/programming/render-partial-if-file-exists/
I might move it to a helper and tweak it a bit to:
<%= render_sidebar %>
# This method could use either the rescue or the if file exists technique.
def render_sidebar
render(:partial => "/#{controller.name}/sidebar"
rescue
#default side bar
end

How to access validation messages in partials?

So, each rails project i seem to run into this problem and would be really grateful if someone could enlighten me:
With a "normal" setup when the form sits on the view immediately associated with the url/controller it is pretty straightforward, a render => :action on fail will display the validation message.
Now what i have is a form in a partial sitting on a page whose url/controller is a show/:id and not the create action of the form, the validation kicks in but i cannot get the validation message to display because i can't trigger the correct render action...
CLosest i got is a render => #object but there is no css/layout, i can pass a message through a redirect with flash[] but it feels wrong, same with jquery/client error messages...
So how can i "cleanly" display validation messages of a form in a partial (under another controller/action than the parent page)?
(thanks in advance for your help)
edit: can't paste the actual thing now but i'll do my best to explain
i have a main page e.g. article/show/01, on this page is the content of the article (#article) and then at the bottom of the page is a partial _commentform with a form to post a comment. This form is bound to a Create action of a different controller (comments controller).
Now if the form were on its own "page"/url instead of a partial, say /comment/create, i would jus do:
if #comment.save
redirect_to #comment
else
render => :create
end
and the validation would display normally.
In my case the form is in a a partial on the article/show/01 url, what should be the equivalent to the code above so that on validation fail error messages are displayed on the parent url, like "render article/show/01" ?
I am sure it is easy but i cannot get it to work (i just can redirect on success but cannot display the errors with a render)
I don't think the best way to display validations errors is to render a partial.
IMHO, the best and clean way to display errors messages using the styles/css you or your webdesigner wants is by implementing your own error_messages method in a FormBuilder.
For example, here is the error_messages method I've implemented for my latest project.
Here is an example that will output the errors list in ul/li's with some custom styles...
Just customize this and put your form builder in app/helpers...
class StandardBuilder < ActionView::Helpers::FormBuilder
def error_messages
return unless object.respond_to?(:errors) && object.errors.any?
errors_list = ""
errors_list << #template.content_tag(:span, "There are errors!", :class => "title-error")
errors_list << object.errors.full_messages.map { |message| #template.content_tag(:li, message) }.join("\n")
#template.content_tag(:ul, errors_list.html_safe, :class => "error-recap round-border")
end
end
Then in my forms :
= form_for #post, :builder => StandardBuilder do |f|
= f.error_messages
...
No need to display/render another partial. And that's all :).
If you want to display anything (including error messages) in a partial you have two ways
1 - Define it in the controller action where the partial is called
2 - pass the message as a parameter to the partial
1 - Example
in your controller/action
if #comment.save
redirect_to #comment
else
#messages = "This is a message"
render => :create
end
in your partial
you can access the #message variable
2 - passing the variable to the partial
render :partial => “<partial name>”, :locals => { :param1 => “value”}
<partial name> – name of your partial (Ex /partials/footer)
:params1 – parameter name
“value” – value
hope this helps
thanks
sameera

Update some part of view(div) as progress of controller's action in Rails

I have div tag named update_info in the view my_view.html.erb and update action takes several minute to complete in the controller my_controller.rb.
I'd like to update update_info div tag every seconds as update action's progress.
How can I do this?
Is it possible update view from the action's loop?
Or using Ajax?, if so, how can I access to server-side data and where is the data stored?(I don't know much about Ajax, please explain using Rails' Ajax helper methods if needed)
Tell me detail plz. thanks.
You can use partials for updating certain div.
You can call controller through ajax.
and then render your output using partial.
render :partial => "partial_name", :layout => false, :locals => {:local_var => xyz}
Note : partial name start with '_'
A comment first: you should avoid having several minutes requests (you should use background processing).
Now, depending of what you are actually doing in that request, the standard method is to interrogate the server (throught an ajax repetitive request, other than the current request) about the progress of current task.
You can do this by using the periodically_call_remote function.
To have an idea:
In your view:
<%= periodically_call_remote(
:condition => "check_var == false",
:frequency => 3,
:url => { :action => "evaluate_progress" }) %>
In your controller:
def evaluate_progress
# replace it with code that actually computes the progress
done = 0.6
render :update do |page|
if(done == 1)
page << "check_var = false" #To stop querying the server
else
page << "check_var = true"
page.replace_html('your_div_id', "The task is #{done*100}% complete.")
end
end
end
Again, this is just a proof of concept, i haven't tested it. The actual code depends also on what you are actually doing in the long-lasting request.

Resources