I have an application with a sometimes longer executing action. How might I, if possible, render placeholder text such as 'processing...' before the action executes and replace the page with the result of that action, given that you can only issue a render once per action?
the :text option of the render method accepts a Proc object:
render :text => proc { |response, output|
5.times do |i|
output.write("Hello, friend\n")
sleep 3
end
}
Your example might look like :
render :text => proc {|response, output|
output.wirte("Processing..")
results = perform_something
output.write(results)
}
As you see, the 'output' here is an writable IO-like object. But still, you should avoid using it whenever possible. You can show the 'Processing ...' message with javascript easly here, then receive an xhr back from controller once you have the results to display
Related
I have in my controller something very straightforward:
def show
#group = Group.find(params[:id])
render :text => #group.inspect
end
I know that #group exists because if I send it to view (by commenting out the third line), it shows everything correctly. However, when I includethat render text line, I get simply a "#" and nothing else. What am I doing wrong?
It is rendering correctly if you view the source of the page. The problem is that the output is something like this:
#<Group id: 123, ...>
The browser expects HTML, so when it sees the opening bracket it thinks it's an HTML tag (but it's really not valid HTML).
Instead you could try escaping the HTML first:
render :text => CGI.escapeHTML(#group.inspect)
This will replace < with <, etc., properly displaying what you expect.
I'm doing some controllers to render reports and here is my problem:
The user open a page with a form which let it change the download format and the date of the report.
The download format is set trough a select input.
When the user press the button I want to response depending on the selected format.
The problem is that it's specified trough the url. So trying to do something like:
case format
when "xlsx" then format.xlsx{...}
when "html" then format.html{...}
...
end
doesn't work because rails or the browser (I'm not sure) expects an html response.
I've though of two options:
Change the url of the form onsubmit which makes the application more dependent on javascript. Or.
redirect_to url + ".#{params[:download_format]}"
The second way looks better to me but I need to pass the :report_date in the url and I can't find the way to do it.
I've tried this:
url = my_custom_url_path
redirect_to url + ".#{params[:download_format]}", :date_format => params[:date_format]
But it's not working.
In the form:
<%= f.select :download_format, { "xlsx" => "xlsx, "Online" => "html" } %>
In the controller:
def action
if download_format = params[:download_format].delete
redirect_to my_action_path(options.merge( :format => download_format ) ) and return
end
# do some logic here
respond_to do |format|
format.xlsx{...}
format.html{...}
end
end
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
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.
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.