How can I load a embedded ruby javascript file on pageload? - ruby-on-rails

So, as I am sure you are all familiar with, you can have actions in Rails that call html.erb files. You can also set up actions to render remotely that call embedded ruby files (for example submitting a "post" form to the "posts" controller, handling it remotely, and calling a js.erb file to update elements in the page).
What I want to know is how to run a js.erb file when I'm running an action that loads a template (html.erb file). To explain, consider if I want to run a User Show page:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
end
Linking to user_path(User.find(1)) will load show.html.erb, and all seems great.
But what if I want to click on a link to user_path(User.find(1)) and load show.html.erb while also loading show.js.erb? If (and I hope it is) this is possible, how could I adapt the show definition to also load show.html.erb and custom_js_file_name.js.erb?
FYI: I'm using Rails 3.0.9 and 3.1.3 on two different applications, and assume that I would put show.js.erb or any others in the Users folder (views/users/...)

By default,
def show
#user = User.find(params[:id])
end
will only render the show view based on if it was requested via HTML, JSON, JS, etc.
I think what you are describing is better suited for the render method in Rails. Typically, controllers use the different format.(:format) methods in the respond blocks to respond to a controller call based on what the request type was (JSON, HTML, JS, etc).
In your show.html.erb file:
<%= render "users/show.js" %>
This allows you to render any arbitrary file you want in another one of your views. This also allows you to split up your large view files into smaller (reusable) pieces called partials (note: all partials are named with a _ character at the beginning and are called via <%= render :partial => "users/my_partial" %> which would render the _my_partial.some_format.erb file)

Try having the show action render the show.js.erb file when requested with a format of js. That should get Rails to render the dynamic template; now link to it from the original show.html.erb with a javascript link tag.
In show.html.erb:
<script type="text/javascript" src="<%= show_users_path(#user.id) -%>.js"></script>
I haven't tried this and the rendering of show.js.erb may put additional formatting that would be a problem.

Related

How does ActionView::Template :render decide where to load from?

Here's the problem, let's say I have a few resources that I want index all on the same page separated by tabs, in this case it's different types of content: videos, pdfs, etc. I also have a partial that gets included into several different views, in this case a search functionality. The partial does something like this to let you customize various parts of it:
in _search.html.slim:
.toolbar
.left
= render 'left_toolbar', f: f
The project tree looks something like:
app
--views
----media
------index.html.slim
------videos_list.html.slim
------pdfs_list.html.slim
------videos
--------_left_toolbar.html.slim
------pdfs
--------_left_toolbar.html.slim
And I have a controller that manages requests coming in from the page with the various content-resources:
in media_library_controller.rb:
def index
end
def videos
<sets everything needed to render a videos list, #videos etc.>
render :index
end
def pdfs
<sets everything needed to render a pdf list, #pdfs etc.>
render :index
end
And in the index.html.slim
= render 'application/search'
ul.nav.nav-tabs
li.active
a href='#videosListTab' data-toggle='tab' Videos
li
a href='#pdfsListTab' data-toggle='tab' PDFs
ul.tab-content
li.tab-pane.active#videosListTab
.js-video-collection
= render 'videos_list' if #videos
li.tab-pane.active#pdfsListTab
.js-pdf-collection
= render 'pdfs_list' if #pdfs
What I want is to be able to define in the controller which folder left_toolbar.html.slim is looked for when search.html.slim goes to look for it
I have tried overriding controller_path which doesn't seem to work. prepend_view_path seems promising but it appends the controller name to the end of the view path so that I have something like app/views/media_library/videos/media_library when I set it. Any advice? I don't want to had a local to the search partial.
What I want is to be able to define in the controller which folder left_toolbar.html.slim is looked for when search.html.slim goes to look for it
Perhaps you're getting ahead of yourself:
<%= render "shared/menu" %>
That code will pull in the partial from
app/views/shared/_menu.html.erb.
From what I understand of your question, you want to know how Rails can specify a specific path for your partial? There's no need - partials can be referenced from anywhere in your app.
So when you mention that search.html goes looking for it, you just have to prepend the folder for left_toolbar to the path:
render 'your_path/left_toolbar', f: f
Again, with your media tabs... they're all partials - just use the folder name in the partial path:
= render 'app/views/media_library/pdfs_list' if #pdfs

ruby code is showing as html in template

I have a rails app which uses angularjs. Here I'm trying to render a template from rails controller and pass a resource to the template. But the ruby code for displaying the variables are showing exactly as it is in the html.erb view.
def fail
#order = Order.find(1)
render 'payments/fail'
end
in view
<%= #order.as_json %>
My guess would be that the problem is in the name of your view file. I'd guess that you named it something like fail.html instead of fail.html.erb. Without the .erb suffix, Rails just interprets the file as html text and renders it without interpreting the ruby code.
However, changing the file name isn't quite the correct solution. Since you want to render json instead of HTML you don't need to create a view template, so you should just delete the template file altogether.
All Rails models have an .as_json method automatically, so you can simply modify your controller's fail method like so:
def fail
#order = Order.find(1)
render json: #order.as_json
end
Also if you want to do something fancy and modify the json that is returned, you can define your own as_json method inside the model.

Rails/Ajax/Error Processing

So there are tons of articles about how to do this, but certainly there's a best practice...and I don't know enough to filter out silly solutions, good ones, and best ones.
I simply want to submit my forms via ajax (in a dialog) and get the errors back just like I would without using ajax...meaning I like the rails standard error handeling/flash messages/label classes.
Is the best way to reload the entire partial?
Is the best way to use .js.erb (or coffee) for partial stuff? (If so, can you explain how to use these partials?
Is the best way to parse JSON back into the form somehow?
What else am I missing in my [limited] knowledge base?
The way I'd do it is to render a create.js.erb view like:
$("#my_dialog").replaceWith("<%= j(render 'dialog') %>");
where _dialog.html.erb contains the HTML for the contents of your dialog.
<div id="my_dialog">
<!-- flash stuff etc -->
<%= form_for ... %>
<!-- ... -->
<% end %>
</div>
Your controller, for example, will look something like:
class EntriesController < ApplicationController
def create
#entry = Entry.new(params[:entry])
respond_to do |format|
if #entry.save
format.html { redirect_to #entry }
format.js {} # this will render create.js.erb for js requests
else
format.html { render action: "new" }
format.js {} # this will render create.js.erb for js requests
end
end
end
end
summit like 'dat. If you don't want to reload the whole form you can update or do whatever you want in .js.erb
Using js.erb is the way to go. Here's the rationale:
Reloading part of your page basically defeats the purpose of Ajax - which is to modify the page without having to reload or refresh anything. And parsing JSON would be quite tedious.
Using js.erb lets you easily leverage validations that Rails provides. In your js.erb, you can access anything that you normally would from your controller, including the validation errors, you and you can update DOM elements based on those errors. Since you're modifying individual DOM elements, you don't need to concern yourself over the fact that your form may be inside a partial.

Is there a way to render a different template based on current user without using conditionals

I have two different layouts, one is completely custom and the other is bootstrap. For admins we want to render a bootstrap view and for non-admins we render it normally. For the most part this is pretty straight forward because admins and users don't share many views -- but there are some.
My original idea involved overriding render so that it would check if there's a bootstrap version of a file. So for example there would be _user.html.erb and _user.bootstrap.html.erb which would have bootstrap specific templating.
I'd like to not modify any controllers so ideally, something like render 'form' would behave smartly and check if there's an _form.bootstrap.html.erb, and if there isn't it would fallback to _form.html.erb
First attempt
My first attempt looked something like this
# I don't think this is the actual method signature of render
def render(options=nil, extra_options, &block)
# if it should render bootstrap and options is a string and there exists a bootstrap version
# set it up to render the bootstrap view
super(options, extra_options, &b)
end
Current attempt
I'm thinking about registering a template that basically checks if a file exists and then uses erb. I haven't made any progress towards this yet.
I figured it out. This is how I did it:
This is set in the application controller, with a before_filter :render_bootstrap
def render_bootstrap
return unless bootstrap?
new_action = "#{self.action_name}.bootstrap"
has_bs_view = template_exists?(new_action,params[:controller],false) || template_exists?(new_action,params[:controller], true)
if has_bs_view
self.action_name = new_action
end
end
I decided to extend this even further so that inside of a view like show.bootstrap.html.erb you can still use render "form" without doing render "form.bootstrap". This was done by overwriting the rails render helper.

Rails: What does it actually mean to "render a template"

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.

Resources