I have a rails 2.2.2 app with the following odd problem.
There's a .js.rjs file, which is rendered to produce javascript as the response to a .js format query.
If i do this, it works fine:
page.replace_html "search-results-resources-paginated", :partial => "search/discover_results"
Ie, it renders out the app/views/search/_discover_results.html.erb partial (which in turn calls other partials). That's all working fine.
However, I want to break this down into a couple of steps, where i render the partial into a text string, and then use that string in the javascript (i just want to do this for a bit of extra debugging).
I'm trying to do this like so:
text = render(:partial => "search/discover_results")
page.replace_html "search-results-resources-paginated", :text => text
this is now starting to complain about not being able to find the partial:
ActionView::TemplateError (Missing template search/_discover_results.erb in view path
/app/views:
<project folder>/app/views/community:
<project folder>/vendor/plugins/savage_beast/app/views:
<project folder>/vendor/plugins/exception_notification/app/views:
<project folder>/vendor/plugins/query_reviewer/lib/query_reviewer/lib/query_reviewer/views:
) on line #49 of app/views/search/discover.js.rjs:
(i've formatted the above a little, for clarity)
If i tell render the actual file extension, like so:
text = render(:partial => "search/discover_results.html.erb")
then it finds that first partial, but i get the same problem with other partials this it references. It's like render doesn't know that it's supposed to be looking for html.erb files, rather than just .erb files.
Is there an extra option i need to pass to render to make this work?
thanks, Max
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'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 am using stache for server-side evaluation of Mustache templates. I would like to re-use some of these templates on the client-side from JavaScript using ICanHaz.js, but to do so I need to include them into script tags. I would like to avoid duplicating the templates (DRY), but obviously, the templates must not be evaluated before being sent to the client, so using a simple render :partial invocation like in this (HAML) snippet does not work:
%script{:id => 'project_snippet'}
= render :partial => 'project'
Is there any way to include a partial without evaluating it using the underlying template engine (kind of like a raw include)?
In other places the partial is to be used as regular partial, i.e., evaluation is supposed to happen, so changing the file extension to always avoid evaluation is not an option.
do you need a partial as is or you want it to be rendered as HTML with some placeholders for JavaScript templating? you can pass :locals => { ... } with something to be replaced by JS template engine later i.e.
%script{:id => 'project_snippet'}
= render :partial => 'project', :locals => {:name => '{{{ project_name }}}'}
if as is then read the partial content (but it doesn't look like you want this)
%script{:id => 'project_snippet'}
= File.open("#{path/to}/partial.html.haml", "r").read
Well, it seems that I should have read the stache documentation: There is a tag helper available, so
= template_include_tag 'projects/project'
will do the trick after setting the template base directory in an initializer:
Stache.configure do |c|
c.template_base_path = "#{Rails.root}/app/views"
end
I have a file in:
RAILS_ROOT/public/system/pages
It is a snippet of HTML. I would like to render it, along with other things, in one of my views.
But it seems like when I try to do a render from a view, Rails is always looking for a partial. But it doesn't pick up the file even when I name it with a leading underscore. How can I read and display this HTML snippet within a view?
have you tried with
<%= render :file => 'your/path/', :layout => false %>
inside the erb?
I'm familiar with using Ajax templates to update particular parts of a page, but how do you render with layout when doing so? For example, given a layout:
#foo
= yield :foo
a simple view "show.html.haml":
= render #bar
and a partial:
- content_for :foo
= bar.to_html
... the HTML result would render within the layout and I'd see my bar content, but say I want to use Ajax to update only the #foo div. I create "show.js.erb":
$("#foo").html("<% escape_javascript(render(#bar)) %>");
But the result is nothing, as my _bar partial is rendered but outside of the layout, thus my :foo content is never yielded to. How do I get the JS template to render inside that layout?
I've found two answers, but I wonder if there's a better way still. These answers are for Rails 3, by the way.
1. Use a separate JS layout.
This is probably the "more correct" way, but unfortunately didn't work for my situation as I'll explain shortly.
What I wasn't fully aware of, is that in a JS request, the lookup formats are set to JS and HTML. Meaning that the controller will render the HTML template if the JS template does not exist.
But it will not look to the HTML layout in the same fashion, meaning the HTML template will be rendered, but the content_for block is never yielded to, leading to an empty response.
So to make the simple example above work out-of-the-box. You'd delete "show.js.erb" and add a JS layout, (e.g. "bars.js.erb") in the lookup path, which would look like this:
$("#foo").html("<% escape_javascript(yield(:foo)) %>");
In this way, the HTML template is rendered, but in the JS layout, and the HTML of #foo is swapped out for the new content of the response.
2. Render the HTML content in the JS response block.
However, #1 this was not an ideal answer for me. My app uses many nested layouts, most of which are very similar. To make the above example work I'd have to create a lot of JS layouts, all of which more or less copies of the original HTML layouts. A waste of time, and not at all DRY. So I came up with this solution.
It feels less ideal than #1, and please tell me if there's a more appropriate way. But this is what I came up with:
# in bars_controller.rb
def show
# ...
respond_to do |format|
format.js do
lookup_context.update_details(:formats => [:html]) do
#content = render_to_string
end
render
end
end
end
In this way I temporarily set the mimetype for the template lookup to be HTML, render the content to a variable, then render the JS template:
// show.js.erb
$("#foo").html("<%= escape_javascript(#content) %>");
There is one further complication to this. In my nested layout setup, in the HTML response, the layout calls the rendering of its parent to continue to build the body, leading to the complete page. In my case, I want it to simply return the body content. So while I don't need JS layouts for this solution, I do need to slightly change my layout, like this:
-# my_layout.html.haml
-# (given a parent layout that yields to :body)
- content_for :body do
= yield(:foo)
- if request.xhr?
= yield(:body)
- else
= render :file => "layouts/my_parent_layout"
In this way the parent is not called on a JS request, simply resulting in the body (up to this point in the nested layout stack).