How does ActionView::Template :render decide where to load from? - ruby-on-rails

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

Related

rails 5 - render different links depending on a page

I have 4 links on the page (dresses, skirts, shirts, hats) and I want them to be always different depending on the the page. Let's say we click on hats and the links there would be shirts, dresses, skirts, so the current category we are on won't be displayed.
I found something similar here [1]: Render different show pages with category in ruby on rails
But this is not really what I want, as I need to render few links (not one).
My thinking is to create 4 different partials and render 3 links if the params = to the one we don't want to display. Is that a good thinking, or is there any better way of doing it?
You did not provide the exact code, so based on what I assume you will approximately have, you could do something like this:
# Somewhere in your controller
def index # or any action really
...
#current_category = Category.find(params[:category_name]) " # (Or however you want to refer to your category object.
#all_categories = Category.all # (Or an array of plain strings, which would then better be set in some before_action hook so it is present in each of your different pages.)
...
end
# In your view
(#all_categories - #current_category).each do |category|
<%= render "categories/link_card", category: category %>
end
And then in your partials categories/_link_card.html.erb you can have the code for showing the link to any category, using the input variable category to get the exact details of your category. (E.g. name, url).

How to render different show templates conditionally

I have got a Post model, and a post can be either a Video post (with YouTube link) or an Article post (with body text).
There is an enum post_type that determines the type of Post.
In my show.html.erb view I would like to display a different template file depending on the type of post displayed.
The entire html.erb file content is different for Articles and Videos.
In my controller I want to be able to check the post type and render a differnt file accordingly.
if #post.video?
//render video show view `video_show.html.erb`
else
// render article show view `article_show.html.erb`
end
I've already tried render template: 'posts/video' to render a new posts/video.html.erb file I created but that did not work at all.
What is the best way to do this? I want to follow convention and not use crude partials and a simple if-else to switch between partials.
Just simple render works for you, you can use below code:
Just render with HTML file name if file is in same controller's view
if #post.video?
render 'video_show'
else
render 'artical_show'
end
render works with just the relative file name
if #post.video?
# with a file name posts/video_show.html.erb
render 'posts/video_show'
else
# with a file name posts/article_show.html.erb
render 'posts/article_show'
end

Show top posts in a proper way Rails

I have a method in my posts_controller to display most viewed posts.
def top
#posts = Post.all.order("post.views DESC").page(params[:page]).per(10)
end
In routes I have
resources :posts do
collection do
get :top
end
end
The problem is: when i go to /posts/top i have an error: Missing template posts/top, application/top Do I need to write view files for my every method (top isn't the only one) or I can somehow display them in my index file without duplication of code?
Just render the index template at the end of your method:
def top
#posts = Post.all.order("post.views DESC").page(params[:page]).per(10)
render :index
end
I would suggest you to have a close look to rails layouts and rendering documentation. You will get your answer as well concept behind them. Below is the snippet of doc.
In most cases, the ActionController::Base#render method does the heavy lifting of rendering your application's content for use by a browser. There are a variety of ways to customize the behavior of render. You can render the default view for a Rails template, or a specific template, or a file, or inline code, or nothing at all. You can render text, JSON, or XML. You can specify the content type or HTTP status of the rendered response as well.

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.

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

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.

Resources