I'm probably trying too hard for my first website but i wanted to make a dropdown on a (bootstrap) navbar to be flexible, and show the names of the saved work categories.
This is what i've tried to do in the application.html.erb file :
<ul class="dropdown-menu">
<% #workcategory.each do |workcategory| %>
<li><%= workcategory.name%></li>
<% end %>
Failed with error undefined methodeach' for nil:NilClasson the<% #workcategory.each do |workcategory| %>` line.
This is the workcategories controller :
class WorkcategoriesController < ApplicationController
before_action :find_workcategory, only: [:edit, :update, :destroy]
def index
#workcategories = Workcategory.all.order("created_at DESC")
end
def new
#workcategory = Workcategory.new
end
def create
#workcategory = Workcategory.new(post_params)
if #workcategory.save
flash[:notice] = "Workcategory created"
redirect_to(:action=>'index', :workcategory_id => #workcategory.id)
else
#workcategories = Workcategories.order()
render('new')
end
end
def edit
end
def update
end
def destroy
#workcategory.destroy
redirect_to workcategory_path
end
private
def find_workcategory
#workcategory=Workcategory.find(params[:id])
end
def post_params
params.require(:workcategory).permit(:name)
end
end
Any tips and help are welcome, even non-related to the initial question :) Thank you
If you want it in all ur actions, it is wise to put it in your application_controller.rb.
before_filter :set_work_categories
def set_work_categoriers
#w_categories = Workcategory.all.order("created_at DESC")
end
This should work fine.
Also, a tip.
You can use default_scope {order(created_at: :desc)} in your model WorkCategory.rb
Then you can use this like,
def set_work_categoriers
#w_categories = Workcategory.all
end
I would recommend changing the variable name to #w_categories or else it will conflict with your #work_categories name in index action.
In your application.html.erb file, change
<% unless #w_categories.nil? %>
<ul class="dropdown-menu">
<% #w_categories.each do |workcategory| %>
<li><%= workcategory.name%></li>
<% end %>
</ul>
<%end>
I guess this should do the trick
If we talk about index action, then you just forgot to use appropriate variable:
<ul class="dropdown-menu">
<% #workcategories.each do |workcategory| %>
<li><%= workcategory.name%></li>
<% end %>
Update
If you want to have this in all actions, then initialize #workcategories in before_action:
# your_controller.rb
before_action :initialize_work_categories
def initialize_work_categories
#workcategories = Workcategory.all.order("created_at DESC")
end
Layouts
application.html.erb is a layout, meaning that it will be present regardless of whether you're using the Workcategories controller or not.
If you want to load a variable into the layout, irrespective of which controller is being invoked, you'll need to make sure the #workcategory variable is present.
To do this, you would generally put the #workcategory declaration into the ApplicationController (which most other controllers inherit from):
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_categories
private
def set_categories
#workcategory = ....
end
end
This is the standard way to populate layout-side variables. It's not efficient and only works if your controllers inherit from the Application controller.
Some further pointers would include the following:
1. Object orientation
Ruby, and by virtue, Rails, is object orientated.
This means that everything you do should revolve around objects. This is why Rails has many helpers that seem to work "magically".
They're not magic at all - they just take the objects you pass them, and build pre-baked HTML to give you specific functionality.
A good example are the routes:
#config/routes.rb
resources :controller
The reason this is important is that when you call actions / variables, you need to think of them as objects. This is difficult to do for newbies, but if you can get your head around it, it helps your coding massively.
--
2. Controllers
Following on from the above, you have to remember that your controller is really just a way to manipulate objects.
Thus, if you're calling #workcategory, you need to appreciate where the object is going to come from and how it's going to be populated.
You can make a helper method work_categories and use that. Either define it directly in application_helper - or if you don't want to use helpers you can put this code in your ApplicationController
class ApplicationController < ActionController::Base
def work_categories
Workcategory.all.order("created_at DESC")
end
helper_method :work_categories
end
Then in your view:
<ul class="dropdown-menu">
<% work_categories.each do |workcategory| %>
<li><%= workcategory.name%></li>
<% end %>
</ul>
Related
In my layouts/books I have my navigation code that is hitting all my views for books then yields.
I also have my header code that is hitting all my views for books then yields. Really I only want my header code to hit the index and show but not my order.
I've tried putting something like below around my header to:
<% if #render.header == true %>
<div class="header">
HTML
</div>
<% end %>
Then putting in my views:
<% if #render.header == true/false %>
I end up getting undefined method `header'. I've also tried things like:
What do I need to adapt this into to get this to work? Should I be putting something into my controller?
EDIT: I've also attempted the following in both my layout/books and in index:
<% #render.header = true %>
This leads to undefined meathod "header= "
I can get this to work using a global variable but that has got to be a bad idea.
You can do this:
if #render && #render.header
So after flailing around I went from in my layout/book:
<% if #render.header == true/false %>
To:
<% $header == true/false %>
Which worked but it's global. And finally to updating my controller for both index and show to:
def index
#header = true
end
And then my layout/book to:
<% #header == true %>
So yeah. Variable types.
This is nothing terribly different from what I already pointed out in the comments but here it goes:
First of all, #render just defaults to nil in views. So when you are trying to set the value for header, it fails with undefined method 'header=' for nil:NilClass (because Rails looks for a method called #header= for the nil:NilClass).
Use a plain variable #render_header
You can instead just set a plain variable, such as #render_header, in your controller or view (before rendering header):
<% #render_header = true %>
and then in your header
<% if #render_header %>
# ...
Use a hash for #render = { header: true }
Or, if you really want to use the #render variable, you can just set it to be a hash:
<% #render = { header: true } %>
and then in your header
<% if #render.present? && #render[:header] %>
# ...
This is almost like what you originally wrote.
Set the variable automatically based on controller action
If you need this variable to be set for multiple controller actions, you can look into controller filters. They work just like callbacks but for controllers.
For example, you could do:
class MyController < ApplicationController
before_action :set_render_header_to_true, only: [:new, :show]
# ...
private
def set_render_header_to_true
#render_header = true
end
end
That way, the variable would automatically be set to true for the :new and the :show action. If you had multiple controllers needing to set render_header to true, you could move the set_render_header_to_true method to the ApplicationController. For example, this would work:
class ApplicationController
private
def set_render_header_to_true
#render_header = true
end
end
class MyController < ApplicationController
before_action :set_render_header_to_true, only: [:new, :show]
# ...
end
class MyOtherController < ApplicationController
before_action :set_render_header_to_true, except: :index
# ...
end
Set a custom layout from your controller
You can also specify a specific layout to be rendered based on the controller action:
class MyController < ApplicationController
layout 'with_header'
# Use the line below to render header only for specific actions,
# such as new and edit
# layout 'with_header', :only => [:show, :edit]
# ...
end
You can even define a method to dynamically choose the layout:
class MyController < ApplicationController
layout :determine_layout
def determine_layout
if action_name == :new
'layout1'
elsif action_name == :show
'layout2'
else
'application'
end
end
# ...
end
I don't think i understand your problem. Anyway you can also use content_for. For example, if add to layout/books.html.erb:
<%= yield :header %>
For index/show/etc views with header:
<% content_for :header do %>
<div class="header">
HTML
</div>
<% end %>
I am trying to get a customized dropdown menu on my app so that each of my list instances have their own link on the navigation. So far here's my application.html.erb:
<li class="dropdown">
My Lists<b class="caret"></b>
<ul class="dropdown-menu">
<li><%= link_to "All Lists", lists_path %></li>
<% #lists.each do |list|%>
<li><%= link_to list_path(list) do %>
<%= list.name %>
<% end %></li>
<% end %>
</ul>
</li>
However, I now get an error that says undefined method 'each' for nil:NilClass on the line with #lists.each do |list|. I know this (or a similar) structure works on another page, so I'm thinking it must have something to do with the controllers. My lists_controller performs this action correctly on my lists#index page with these definitions:
class ListsController < ApplicationController
def index
#lists = List.all
#list = List.new
end
...
And I have this in my application_controller currently to mirror that:
def application
#lists = List.all
#list = List.new
end
But I'm pretty sure that's not right. What should I have in my application_controller to get it to recognize this in my menu on all the pages?
First, be wary of performing the List query each time the a page is rendered on your app and either cache the query or use manual links if at all possible to avoid any performance problems.
That being said...
You have a few options one of which being a before_action in your application_controller that sets any instance_variables you would like present.
ApplicationController < ActionController::Base
before_action :set_lists
def set_lists
#lists = List.all
end
end
You could also add a dropdown_lists method to your ApplicationHelper file similar to:
def dropdown_lists
List.all
end
which you would use as a replacement to #lists in your view similar to: dropdown_lists.each.....
Also (as an aside) you probably want to move this header information to a partial to keep your layout file clean, but thats not really part of the answer :)
Hope this helps!
I think I know what is wrong here. I believe that you are using application.html.erb incorrectly.
application.html.erb is for generally for placing html that will be displayed on all of your web pages. Think of a header menu as an example.
For your list, it looks to me like you want to display your links on your index action which would be `app/views/list/index.html.erb'.
So I would do this:
Remove the def application method from application_controller.
Create an index.html.erb file for app/views/list` directory.
Now transfer your code in application.html.erb to app/views/list/index.html.erb
That should do it.
For your context: This is my first attempt to create a app. I have just started coding:-).
I am trying to get a simple CRUD setup to work.
Now i'm having two problems i can't get my head around:
My entries don't show up on my index page. it gives te following error: 'undefined method `title' for nil:NilClass'. The model contains the following columns:
string :title,text :forecast, date :review_date
If i go to decisions/edit it gives me the following error: 'Couldn't find Decision with 'id'=edit'
This is my code:
Controller:
class DecisionsController < ApplicationController
before_action :find_decision, only: [:show, :edit, :update]
def index
# gets all rows from decision table and puts it in #decision variable
#decisions = Decision.all
end
def show
# find only the decision entry that has the id defined in params[:id]
#decision = Decision.find(params["id"])
end
# shows the form for creating a entry
def new
#decision = Decision.new
end
# creates the entry
def create
#decision = Decision.new(decision_params)
if #decision.save
redirect_to #decision
else
render 'new'
end
end
# shows the form for editing a entry
def edit
#decision = Decision.find(params["id"])
end
# updates the entry
def update
end
def destroy
end
private
def find_decision
#decision = Decision.find(params["id"])
end
def decision_params
params.require(:decision).permit(:title, :forecast, :review_date)
end
end
index view
<h1>Hello World ^^</h1>
<% #decisions.each do |descision| %>
<p><%= #decision.title %></p>
<% end %>
routes.rb
Rails.application.routes.draw do
resources :decisions
root 'decisions#index'
end
I have been working on these two all morning but i can't figure it out. I would be a great help if you guys can take a look for me.
I have just started coding
Welcome!!
My entries don't show up on my index page.
I'm sure you mean decisions, right?
If so, you have to remember that if you're calling a loop in Ruby, you'll need some conditional logic to determine if it's actually populated with any data before trying to invoke it:
#app/views/decisions/index.html.erb
<% if #decisions.any? %>
<% #decisions.each do |decision| %>
<%= content_tag :p, decision.title %>
<% end %>
<% end %>
This will have to be matched by the appropriate controller code:
#app/controllers/decisions_controller.rb
class DecisionsController < ApplicationController
before_action :find_decision, only: [:show, :edit, :update, :destroy]
def index
#decisions = Decision.all
end
def show
end
def new
#decision = Decision.new
end
def create
#decision = Decision.new decision_params
#decision.save ? redirect_to(#decision) : render('new')
end
def edit
end
def update
end
def destroy
end
private
def find_decision
#decision = Decision.find params["id"]
end
def decision_params
params.require(:decision).permit(:title, :forecast, :review_date)
end
end
This will give you the ability to call #decisions and #decision in your views depending on which route you're accessing.
An important point is that when you say...
decisions/edit it gives me the following error: Couldn't find Decision with 'id'=edit'
... the issue is caused by the way in which Rails routing is handled:
Because Ruby/Rails is object orientated, each set of routes corresponds to either a collection of objects, or a member object. This is why routes such as edit require an "id" to be passed - they're designed to work on member objects.
As such, when you access any "member" route (decisions/:id, decisions/:id/edit), you'll have to provide an id so that Rails can pull the appropriate record from the db:
#app/views/decisions/index.html.erb
<% if #decisions.any? %>
<% #decisions.each do |descision| %>
<%= link_to "Edit #{decision.title}", decision_edit_path(decision) %>
<% end %>
<% end %>
I can explain a lot more - the above should work for you for now.
I have question about helper_method.
When I add fuc in /controller/application_controller.rb
helper_method :values
def values
#food = Food.all
#food_type = FoodType.all
end
I want to use var on /layouts/application.html.erb
code here
<% #food.each do |fp|%>
<p><%= fp.name %></p>
<% end %>
<% #food_type.each do |ft|%>
<p><%= ft.name %></p>
<% end %>
but it return nill .
Please teach me ,thx a lot
You can try this
application_controller.rb
def values
#food = Food.all
#food_type = FoodType.all
[#food, #food_type]
end
application.html.erb
<%food, food_type = values%>
<% food.each do |fp|%>
<p><%= fp.name %></p>
<% end %>
<% food_type.each do |ft|%>
<p><%= ft.name %></p>
<% end %>
DRY
You'll be much better using the before_action callback
The problem you have is a helper is meant as a simple way to create functionality for your application. IE that you pass some data to the helper, it will "crunch" the data, and return a reasonable response.
This is why Vimsha's answer has been accepted - he has made it so that your helper method will return the values you need; consequently meaning you need to call the method if you want to use the data it has .
--
The real answer for you (considering you just want to be able to use the #food and #food_type variables in your application layout), is to use a callback which sets these variables each time:
#app/controllers/application_controller.rb
Class ApplicationController < ActionController::Base
before_action :set_vars
private
def set_vars
#food = Food.all
#food_type = FoodType.all
end
end
Although relatively inefficient (you'll be best caching this data so it won't be pinging your DB each time), you'll be able to use #food and #food_type in your application layout with no issues
To generalize my problem, I am using an API that returns an iterable object. Within that those is an id for each object. My controller looks like this:
class SearchController < ApplicationController
def index
#search = API.find(params[:query])
end
end
My view is something like this:
<% #search.each do |thing| %>
<h2><%= thing.attr2 if thing.attr1 %></h2>
<%= API.list(thing.attr2) %>
<% end %>
I have tried adding a method into
class SearchController < ApplicationController
def index
#search = API.find(params[:query])
def getList(attr2)
API.list(thing.attr2)
end
end
end
and adding index and self before the definition (ex: self.getList(attr2)) and calling it in all those variations in the view:
<%= getList(thing.attr2) %>
I am wondering where I am going wrong here. I have additionally tried to add in the helper_method line as I read in a few docs but it would not recognize it. Also, would this be the correct way to go about this style-wise? Having a hard time finding references for it makes me think this isn't standard practice.
The method I was trying to make is a helper method, and therefore, needs to go in the helper method for the controller.