Nested layout inside application layout - html

I have little problems with nested layouts. On my site I need to make one separate part of the site only for administrator.
I have this in my application.html.erb file:
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
<%= render 'layouts/footer' %>
</div>
</body>
I was wondering how can I now make another template like this to be inserted inside <%= yield %> because for administrator part I again need fixed parts of the site like header and footer in main layout. Instead of header and footer I will have two menus. I want <%= yield %> to be filled with new template which will have menu on top and new <%= yield %> which will be filled with actions from admin controller. So, menu will always stay on top.
I've made a menu partial views/admins/_menu.html.erb:
<div>
<div>
<div class="container">
<ul>
<li><%= link_to "Action1", '#' %></li>
<li><%= link_to "Action2", '#' %></li>
<li><%= link_to "Action3", '#' %></li>
</ul>
</div>
</div>
</div>
My new layout is layouts/sublayouts/admin.html.erb:
<%= render 'admins/menu' %>
<%= yield %>
Currently alternative is to render views/admins/_menu.html.erb in each view on top but that doesn't look as a good solution to me.
Regular site would have this structure:
Header/Menu
|
Container
|Content
Footer
And admin site would have this structure:
Header/Menu
|
Container
|Content
|Admin Menu
|Admin Content
|
Footer
What would be the best way to accomplish this?

Update: Based on the comments I've updated the answer with a better understanding of the question
The best way is to incorporate this into your application.html.erb layout.
The desired behavior is to have the admin menu appear when the user clicks on the Admin Panel link or any links on the admin menu.
The way I recommend doing this is that you have an admin controller which handles routing to all of your admin views, so clicking on the Admin Panel button and all the links in the admin menu will be handled by your admin controller. Add a before_filter to you admin controller like this:
# app/controller/admin_controller.rb
class AdminController < ActionController::Base
before_filter: set_admin_status
private
def set_admin_status
#admin = true
end
end
In your application template do the following:
# application.html.erb
<body>
<%= render 'layouts/header' %>
<div class="container">
<% if #admin %>
<%= render 'admins/menu' %>
<% end %>
<%= yield %>
<%= render 'layouts/footer' %>
</div>
</body>
What this should do is that everytime you navigate to the page that corresponds to the Admin Panel or any of the links in your admin menu it will set the #admin_status flag to be true and your layout will render the admin menu, which I believe is the desired behavior.

Usually I do following to accomplish the same problem, I create the separate layout based on parent class:
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
layout :layout
private
def layout
if self.class.parent == Admin
'application_admin'
else
'application'
end
end
end
app/views/layouts/application.html.haml
Header/Menu
|
Container
|Content
Footer
app/views/layouts/application_admin.html.haml
Header/Menu
|
Container
|Content
|Admin Menu
|Admin Content
|
Footer
Update 1
config/routes.rb
namespace :admin do
root to: 'home#index'
resources :admins
end
app/controllers/admin/admins_controller.rb
class Admin::AdminsController < ApplicationController
def index
// code
end
end

Related

Whole page reloads when navigating using Turbo in Rails application

I recently began working on a documentation site using Ruby on Rails and Turbo (repository is here, and the website is here), and I'm having a slight issue with navigation.
When I press the links in the left sidebar, the content on the right side of the screen is correctly replaced. Here's some relevant code -
articles.html.erb
<div class='docs-split'>
<div class='sidebar'>
<div>
...
<div class='links padding-top'>
<%= link_to render(:partial => 'shared/primary_navigation_link', locals: { title: "Getting Started", path: "getting-started" }), 'getting-started' %>
<%= link_to render(:partial => 'shared/primary_navigation_link', locals: { title: "Types of Charts", path: "types-of-charts" }), 'types-of-charts' %>
</div>
</div>
</div>
<div class='content'>
<%= turbo_frame_tag "article", src: article_path(params[:title] ? params[:title] : "getting-started") do %>
<% end %>
</div>
</div>
articles_controller.rb
require 'github/markup'
class ArticlesController < ApplicationController
def index
end
def open_article
title = Rails.root.to_s + "/app/markdown/" + params[:title] + ".md"
#content = ActionController::Base.render inline: GitHub::Markup.render(title, File.read(title))
end
end
open_article.html.erb
<%= turbo_frame_tag "article" do %>
<%= #content %>
<% end %>
routes.rb
Rails.application.routes.draw do
root :to => redirect('/articles/getting-started')
get 'articles/:title', to: 'articles#index'
# Embedded article GET route
get ':title', to: 'articles#open_article', as: 'article'
end
What I expect to occur is that only the content inside the Turbo tag will change while the rest of the HTMl remains the same and the navigation link styles will transition as specified by my CSS. However, what seems to occur instead is that all of the HTML is reloaded, which means the CSS transitions that are supposed to happen when navigation links are clicked don't happen and the Turbo tag content appears to blink twice.
I'm new to Rails and Hotwire/Turbo, so any help would be appreciated. Thank you!

Links in navbar with Rails and Bootstrap

Say I have a navbar:
<ul class="nav navbar-nav">
<li><%= link_to 'Item 1', item1_path %>
<li><%= link_to 'Item 2', item2_path %>
...
But this way current menu item is also a link. How do I make it into a span, preserving appearance? Which doesn't look like a good options, since in this case I need to duplicate bootstrap's styles. Or make it look like a menu item without it pointing to any page?
A link_to method will always render an <a> tag, but you can render a <span> inside of the <a> tag if you use link_to ... do
This is an example taken from this documentation
<%= link_to(#profile) do %>
<strong><%= #profile.name %></strong> -- <span>Check it out!</span>
<% end %>
<!-- which renders -->
<a href="/profiles/1">
<strong>David</strong> -- <span>Check it out!</span>
</a>
I came up with the following helper:
module ApplicationHelper
def menu_item(name, path)
if current_page? path
content_tag('a', name)
else
link_to name, path
end
end
end
With that we've just got to replace link_to with menu_item in original code.

Rendering view for an element in rails by :id

I have a sidebar that contains links to all of a users :shopping_lists. Upon clicking on one of those links, I'd like to render a page showing the :list_items in that particular list. Here's my sidebar partial:
<aside class="sidebar-nav-fixed">
<h1>My Lists</h1>
<% if user_signed_in? %>
<% current_user.shopping_lists.each do |l| %>
<ul>
<%= link_to "#{l.name}", '#' %>
</ul>
<% end %>
<% else %>
<h5><%= link_to "Sign in to manage lists.", new_user_session_path %></h5>
<% end %>
</aside>
My question is: what path would I be putting in place of my current stub link in order to route to the correct list? Thanks in advance!
That will depend on how your routes are setup. I would expect shopping lists to always be in the context of a user, so probably something like this:
<%= link_to l.name, user_shopping_list_path(current_user, l) %>
If shopping lists are a top level route, then probably something like this:
<%= link_to l.name, shopping_list_path(l) %>
There are couple of things you can do, granted your routes are setup correctly:
The easiest is:
link_to "#{l.name}", l
Rails should create a link something similar to http://host/shopping_lists/2
The above is a shorthand for
link_to "#{l.name}", shopping_list_path(l)
To see a list of available routes and methods you can run:
bundle exec rake routes
in the root of your rails app

Spree User information

I'm using Spree as an eCommerce solution for a website that I am building and need to customize the layout, the deface option is great but I already have a layout for the rest of my application that I want to use so there is no change between the main part of my site and the store.
Having looked through the spree source code I have been able to transfer most of the infrastructure over to my own new template but I have hit one stumbling block. In the default template above the search box there are login/account and logout links which based on some digging through the code is rendered in the 'header' partial which itself renders the nav bar which renders the search bar partial, and the main nav bar which shows the links to home and the shopping cart, but nowhere mentions the user in/out stuff. Commenting out the rendering of the header partial removes all of this stuff but also the user information, which as far as I can tell isn't mentioned anywhere...
Does anyone know where in the default spree layout it implements this code for showing a login/logout account link, I could do this relatively simply with basic links to it but would rather understand how Spree implements this itself and I'm trying to keep my layout compatible with any updates to the core code.
Any help would be greatly appreciated, thanks!
Edited: For clarity
On version 3.0.1 of Spree and maybe on other older versions the user functionality is not handled by the core itself. Users are handled by the Spree Auth (Devise) Spree extension. This extension is included by default on new Spree apps. The extension uses a deface override to add this functionality. Interestingly enough the partial used by this override is not on the extension itself but rather on the Spree frontend files. I assume to make this component reusable should you wish to implement your custom user module. This is the code of the partial:
<% if spree_current_user %>
<li><%= link_to Spree.t(:my_account), spree.account_path %></li>
<li><%= link_to Spree.t(:logout), spree.logout_path %></li>
<% else %>
<li id="link-to-login"><%= link_to Spree.t(:login), spree.login_path %></li>
<% end %>
Found code in the RDR theme that explains this, still not sure why it doesn't show up in the default spree code, maybe a data hook?
The way RDR does it:
<% if current_user %>
<%= link_to t('logout'), destroy_user_session_path, :class => "cart" %>
<%= link_to t('my_account'), account_path, :class => "cart" %>
<% else %>
<%= link_to t('log_in'), login_path, :class => "cart" %>
<% end %>
override menu with
<% if current_user %>
<%= link_to t(:logout), spree.destroy_user_session_path %>
<%= link_to t(:my_account), spree.account_path %>
<% else %>
<%= link_to t(:login), spree.login_path %>
<% end %>
As of Spree 2.3.2 same thing can be done with:
<h2>Your Account</h2>
<ul>
<% if spree_current_user %>
<li><%= link_to t('My Account'), account_path, :class => "cart" %></li>
<% else %>
<li><%= link_to t('Log In'), login_path, :class => "cart" %></li>
<li><%= link_to t('Sign Up'), registration_path, :class => "cart" %></li>
<% end %>
</ul>

Placing link at top

How do I place a link at the top of my page when the URL that it is pointing to is not determined until later down the page. In this example, I want to move Create and Edit Scenario links to the top of the page, but as you can see Edit Scenario depends on knowing the #scenario_id first.
<%= will_paginate #scens, :next_label => 'Older', :prev_label => 'Newer' %>
<div class="box">
<% for scenario in #scens %>
<% #created = scenario.created_at %>
<% #updated = scenario.updated_at %>
<% #scenario_id = scenario.id %>
<% if scenario.scenario_image.exists? %>
<%= scenario_image_tag(scenario) %>
<% end %>
<%= simple_format(scenario.description) %>
<% end %>
</div>
<% if session[:role_kind] == "controller" %>
<p>
<%= button_to "Create new scenario", :action => "create" %>
<% if #scens.size > 0 %>
<%= button_to "Edit scenario", :action => "edit", :id => #scenario_id %>
<% end %>
</p>
You can add the link at the top but you will need to programmatically access it later and then assign the URL to it. That needs some kind of reference or look-up capability, I'm thinking client-side javascript but that's as I don't know Ruby.
Alternatively you could create the link later when you have the URL and place the link at the top using CSS positioning. The actual position of all the DOM elements on the page need not match the order in which they are rendered.
One way to do this is to use a helper:
In your helper.rb file:
def stack_example(scens, &block)
html = 'Scenario Details'
edit_link = 'Edit Link'
yield html, edit_link
end
Then in your partial you could have something like:
<% stack_example(#scens) do |html, edit_link| %>
<%= edit_link %><br>
<%= html %>
<% end %>
Should output the following:
Edit Link
Scenario Details
I don't get it. Why do you create model in the view layer? Why wouldn't you create the model variables in the controller? Sth like:
class your_controller
def your_method
#scenario_id = ...
end
end
I think that your problem lays in the invalid MVC usage. Don't you think that all the #member #variables should be initialized before the view starts to render?