Help me understand dynamic layouts in Sinatra - ruby-on-rails

Help me understand this; I'm learning Sinatra (and Rails for that matter, er, and Ruby).
Say I'm doing a search app. The search form is laid out in one div, and the results will be laid out in another. The search form is rendered into the div by a previous view (maybe from a login form).
I want to process the form params, perform the search, and render the results into the results div.
If I have a single "yield" in the layout and render the divs from different views, the results div erases the search div when it renders.
If I define the divs in the default layout, then just render the content, obviously the layout will be messed up: there would have to be two "yields" and I don't think Sinatra supports passing blocks in to yields.
I tried foca's sinatra-content-for plugin, and that seems closer to what I need. But I can't figure out where to place the "yield_content" statements.
If I have this haml in my layout:
#search
-# search form
= yield_content :search
#results
-# search results
= yield_content :results
... this in my search view:
- content_for :search do
%form{:method => "post"... etc.
... and this in the results view:
- content_for :results do
%table{:class => 'results'... etc.
This sort of works but when I render the results view, the search div is emptied out. I would like to have it remain. Am I doing something wrong? How should I set this up?

I think you mean you ALWAYS want to show 2 divs, but in a new search they should be empty and on a results page they should be populated. You can probably get away w/ one haml template, and just populate it diff'tly on the request method:
get "/search" do
# render haml
end
post "/search" do
# set instance variables: #search & #results
# run search
# render haml
end
(Sorry this is very pseudo... not at a real computer.)

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).

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.

rails rendering partial with collection / other class is in charge?

Somewhat new to rails, longtime programmer. I've got a question about views, controllers and partials really - wondering if I have this setup well.
I've got a pages controller, and on the index page (really the pages index method) I've got a partial in layouts called featured (ie app/views/layouts/_featured.html.erb) -- I've also got a Featured class. I would like basically the index of the featured class to be drawn here. But of course it's not working. SO the question is:
In the page itself I've got the <%= render 'features/index' %> which I'm beginning to think is the wrong way to go..
Do I axe this partial method and just call <%= render 'features/index' %> and let everything progress natively or
What would be the proper way of routing the featured collection to the partial? Since the controller is actually Pages it seems like I'm fighting against the tide.
<%= render 'features/index' %>
Doing this is wrong given your description. This will try to render a partial from app/views/features/_index.html.erb which you haven't mentioned.
To render the partial at app/views/layouts/_featured.html.erb you would do (perhaps a bit more verbose that is necessary)
<%= render partial: "layouts/featured" %>
The best suggestion I can offer is to pass a collection to this partial
<%= render partial: "layouts/featured", locals: { features: #features } %>
Since it seems your intention is for this partial to appear as a piece of a layout I will assume you wish for this partial to appear on multiple pages. This means on multiple actions you will need to have assigned the set of Feature instances this #features instance variable. One way to do this is a before_action.
before_action :setup_features
# ...
private
def setup_features
#features = Feature.all
end
A good place to start learning more about filters is in the Rails Guide
The partial at "app/view/layouts/_featured.html.erb" can only be rendered with
render 'featured'
and not 'featured/index'
render 'featured/index' will render "app/views/layouts/featured/_index.html.erb
Since the pages controller is really rendering the main index page in it's def index, all I had to do was #features = Feature.all and the variable is available for the partial pulled into the index page.
I need to get used to how simple rails is coming from other languages / frameworks.

What is the difference between render and yield in Rails

Can someone explain the difference between "<%= render %>" and "<%= yield %> with <% content_for :partial do %>/<% end %>"? specifically how the routing changes when switching from one to another, the benefits of using one over the other, when is it practical to use one over the other. THIS is the closest explanation I have found, but isn't quite clear enough for me.
I have been trying for several days to wrap my head around this, but it seems that each configuration I try either comes close, or errors out.
If theres are three views, aaa and bbb and ccc, and each has an index.html.erb, but bbb and ccc have a _content.html.erb partial (signified by the underscore) how can you accomplish getting the bbb or ccc partial in aaa using either render or yield?
The following works:
aaa's index.html.erb :
<div">
<%= render 'bbb/content' %>
</div>
and bbbs _content.html/erb :
<p>Content from bbb.</p>
BUT this does NOT:
aaa's index.html.erb :
<div">
<%= yield :container %>
</div>
and bbbs _content.html/erb :
<% content_for :container do %>
<p>Content from bbb.</p> ### viewed in aaa
<% end>
and cccs _content.html.erb would have nothing, or the content_for, but I still dont get aaa's index.html to be populated with content.
If I use the render, I can explicitly place the content in. But I thought that the benefit of using the yield :whatever would allow me to choose what to populate it with, and I can't get it to populate anything as soon as I change it from render to yield. Do I also have to update the routes file? If so, how do I choose which one to populate it with? Does that mean its in the controller? and needs an action?
I also have though that it depends on which file is initially routed to, but like I said, I think I need to understand the difference between the two before I can begin to use the partials to my advantage.
First of all, yield is ruby, render is rails. Usually one uses a common layout for the application whose inner content changes according to action/context. The problem usually lies in defining where our layout ends and context-specific template begins. Take, for instance, the HTML title tag. Let's say you have an application called Cities. In most cases, you want your page title to be "Cities" all the time. But, if you're for instance, inside Amsterdam page, then you would like the have "Amsterdam" as your page title.
# application.html.erb
<html>
<head>
<%= content_for?(:page_title) ? yield(:page_title) : "Cities" %>
......
# city/index.html.erb
<% content_for :page_title do %>
<%= #city.name %>
<% end %>
<div class="bla"...
Within Rails you usually define your application title in your application layout. One strategy for changing the page title would be to use content_for in the specific cities template and change accordingly.
Render, on the other hand, accomplishes different rendering strategies. Straight. When you call render, it renders. content_for/yield doesn't render automatically, it is stored somewhere and then fills up the missing spots in the due places. So, you can think of it as more as a "store/search/replace" in comparison to render, which just plain renders.
Good rule of thumb to use one over the other is: if the template you are writing needs to present different information per context, strongly consider using content_for.
yield
Ruby code (Proc class) and takes your block and does what it is supposed to do with it. Yield is also fast compared with other Ruby based ways of doing the same thing.
I'd assume (and I only) use it in the layouts because it's quick and I mindlessly do what's normal in Rails. yield is also used to pass content to a specific spot in your layout. I often have <%= yield :head %> in the head, just above the head tag, so that I can pass random weirdness that sometimes comes up.
Common Uses:
Mostly just used in layouts
(if you are fancy/inclined to do so in a Model) as a true Ruby Proc
statement.
render
Rails code that you pass arguments to that, as the docs say, "Renders the content that will be returned to the browser as the response body". partials, actions, text, files...etc.
Common Uses:
Used in both views and the controller.
When your controller method exits, it renders the associated file. So the edit controller renders edit.html.erb. It uses the specified layout or application.html.erb if none is specified.
Within your layout file, when you call yield it will fill in the information from your render. If you call yield with a parameter, it will look for a content_for section in your render file matching that parameter. I'm not completely sure, but I don't think you can call yield from outside of your layout file, and I don't think it will fill in any information except that found in your render file.
Anywhere in your layout file or your rendered file, you can render a partial by calling render with the partial name minus the underscore.
I hope that helps.
Edit to answer question in comment:
yield and render perform similar functions however yield only looks in the render file whereas render specifies which file to render. Also, render outputs the entire file, but yield with a parameter can output just a subsection of the file.
Here's a visual to put them both in perspective:
The render method is called at the end of a controller action and orchestrates what block is passed to the method that is actually rendering the application.html.erb by yielding the passed block.
https://richstone.io/debunk/

Rails: How do you render with a layout inside a JS template?

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).

Resources