NoMethodError for nil after rendering view in controller - ruby-on-rails

So I have this in my view _form.erb:
<div class="form-group">
<%= f.label :start_hour %><br>
<%= f.select :start_hour, #select_hours.map {|value| [value, value]} %>
</div>
And this in edit.erb:
<%= render 'form' %>
And this in my controller
def edit
#user = current_user
#employee = #user.employee
#hour = #employee.working_hours.find(params[:id])
#select_hours = Array.new
for i in 0..12
#select_hours.push("#{07+i}:00")
#select_hours.push("#{07+i}:30")
end
end
And then my update in my controller
def update
#employee = current_user.employee
#hour = #employee.working_hours.find(params[:id])
if #hour.update(working_hour_params)
redirect_to employee_working_hours_path(#employee)
else
render :edit
end
end
And here's my problem:
When I click update AND have wrong start_hour (custom validation, works when creating not editing), so #hour will not update. It renders again this view but with error that there is no method map for nil (so for #select_hours).
So how can I fix this?

You can use a callback in your controller and set up #select_hours for those two actions, this way if the update fails, the value will be present, but you don't have to assign the variable twice, like:
before_action :set_select_hours, only: %i[edit update]
before_action :set_employee, only: %i[edit update]
before_action :set_hour, only: %i[edit update]
def edit; end
def update
if #hour.update(working_hour_params)
redirect_to employee_working_hours_path(#employee)
else
render :edit
end
end
private
def set_select_hours
#select_hours = (0..12).flat_map do |index|
["#{07 + index}:00", "#{07 + index}:30"]
end
end
def set_employee
#employee = current_user.employee
end
def set_hour
#hour = #employee.working_hours.find(params[:id])
end
I think #employee can also be setted within a before callback.
I've added flat map to create and fill an array starting from the range, it's the "same" as before, just you don't need to initialize the array, use the for loop, and push the content for it.

Related

Rails new initialized object create an empty record

I have two model called TodoList and TodoItem. In the TodoItem index page, i'm showing new form and list of todo items. Everything works perfect But it generate an empty record while in browser.
class TodoItem < ApplicationRecord
belongs_to :todo_list
end
class TodoList < ApplicationRecord
has_many :todo_items, dependent: :destroy
end
controllers have:
class TodoItemsController < ApplicationController
def index
#todo_list = TodoList.find(params[:todo_list_id])
#todo_items = #todo_list.todo_items
#new_todo = #todo_list.todo_items.new
end
def create
#todo_list = TodoList.find(params[:todo_list_id])
#todo_item = #todo_list.todo_items.new(params.require(:todo_item).permit(:description, :complete_at))
if #todo_item.save
redirect_to todo_list_todo_items_path(#todo_list)
end
end
end
index.html.erb
<div>
<div>
<% form_with(model: [#todo_list, #todo_item], local: true) do |f| %>
<% f.text_field :description %>
<% f.submit %>
<% end %>
</div>
<ul>
<% #todo_items.each do |todo_item| %>
<li><%= todo_item.description %></li>
<% end %>
</ul>
</div>
class TodoItemsController < ApplicationController
# use callbacks instead of repeating yourself
before_action :set_todolist, only: [:new, :create, :index]
def index
#todo_items = #todo_list.todo_items
#todo_item = TodoItem.new
end
def create
#todo_item = #todo_list.todo_items.new(todo_list_params)
if #todo_item.save
redirect_to [#todo_list, :todo_items]
else
render :new
end
end
private
def set_todolist
#todo_list = TodoList.find(params[:todo_list_id])
end
# use a private method for your params whitelist for readibility
# it also lets you reuse it for the update action
def todo_list_params
params.require(:todo_item)
.permit(:description, :complete_at)
end
end
You where setting a different instance variable (#new_todo) in you index action. The polymorphic route helpers that look up the route helpers from [#todo_list, #todo_item] call compact on the array. So if #todo_item is nil its going to call todo_lists_path instead - ooops!
You alway also need to consider how you are going to respond to invalid data. Usually in Rails this means rendering the new view. If you are rendering the form in another view such as the index view it can get kind of tricky to re-render the same view as you have to set up all the same data as that action which leads to duplication.
It seems #new_todo has been added to #todo_items somehow in index action:
def index
#todo_items = #todo_list.todo_items
#new_todo = #todo_list.todo_items.new
# The above line has a side effect: #todo_items = #todo_items + [#new_todo]
end
I'm not sure it's a bug or feature from Rails (I use Rails 6.1.1).
For a quick fix, you can change #todo_list.todo_items.new to TodoItem.new.

Passing instance variable from controller to view in rails

I have a user profile controller called "userinfo" and it's corresponding view. The userinfo index is the root path. In the homepage(which is the userinfo index), I have a link that takes you to the user profile page. It is giving me this error when I click on the image on the view page:
My routes are:
My userinfos_controller:
class UserinfosController < ApplicationController
before_action :find_userinfo, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#userinfors = Userinfo.where(:userinfo_id => #userinformation_user_id)
end
def show
#myvideo = Video.last
end
def new
#userinformation = current_user.userinfos.build
end
def create
#userinformation = current_user.userinfos.build(userinfo_params)
if #userinformation.save
redirect_to root_path
else
render 'new'
end
end
def edit
end
def update
end
def destroy
#userinformation.destroy
redirect_to userinfo_path
end
private
def userinfo_params
params.require(:userinfo).permit(:name, :email, :college, :gpa, :major)
end
def find_userinfo
#userinformation = Userinfo.find(params[:id])
end
end
and my view is:
<%= link_to image_tag("student.png", class: 'right'), userinfo_path(#userinfors) %>
I thought maybe I must include ':index' in the 'before_action :find_userinfo' at the top of my controller. If I do that, the homepage doesn't even load and it gives me this error:
Try below code:
controller
def index
#userinfors = Userinfo.where(:userinfo_id => #userinformation_user_id) #pass id instead of object #userinformation_user_id
end
view
<% #userinfors.each do |u| %>
<%= link_to image_tag("student.png", class: 'right'), userinfo_path(u) %>
<% end %>
Your problem is that you're trying to do perform a lookup based on something that's not an ActiveRecord (database) attribute.
Your root goes to UserinfosController which expects #userinformation_user_id but I can't tell from your code where that comes from.
You need to define your route in order that this will be expecting for an specific param, maybe the user id, and then you're able to add the value within your view, in a link_to helper:
You could modify your routes.rb to expect an id as param:
get '/user_infors/:id', to: 'userinfos#index', as: 'userinfo_path'
Then in your controller, use a find to "find" in the database the user with such id. If you'd like to use where then that would give you a relationship with all the userinfos with the id being passed as param.
If you want so, then use Userinfo.where('userinfo_id = ?', params[:id]):
def index
#userinfors = Userinfo.find(params[:id])
end
And then in your view you can access to #userinfors:
<% #userinfors.each do |user| %>
<%= link_to image_tag 'student.png', class: 'right', userinfo_path(user) %>
<% end %>
I think you could define the index to get all the userinfors and a show method to get an specific one, as you're trying to do.

Undefined method `each' for nil:NilClass for show action

I cannot seem to find the problem. In my venues show template, I want to show the venue name, and under that, I list all the venues in the database
<%= venu.name %>
<% #venus.each do |v| %>
I get the error that #venus is nil... but it is defined in my controller:
undefined method 'each' for nil:NilClass
venues_controller.rb
class VenuesController < ApplicationController
before_action :find_venue, only: [:show, :edit, :update, :destroy]
def index
#venus = Venue.all
end
def show
render :layout => nil
#venus = Venue.all
end
def new
#venu = Venue.new
end
def create
#venu = Venue.new(venue_params)
#venu.save
end
def edit
end
def update
end
def destroy
end
private
def venue_params
params.require(:venue).permit(:name, :phone, :address, :description, :type)
end
def find_venue
#venu = Venue.find(params[:id])
end
end
I have a resources :venues route in my routes.rb.
I am not sure what is causing this problem.
In your show method, you should render at the end
def show
#venus = Venue.all
render :layout => nil
end
Remove render :layout => nil from your show action.
And in your view, you need to use the instance variable #venu instead of venu
<%= #venu.name %>
I wonder why you use two instance variables for action show
#venu (via find_venue before_filter) & #venus via the action itself.
Best practice would be removing this line from action show, since show action normally used to show details for one element from a list.
#venus = Venue.all
and use #venu set by the before_filter instead.
But if you do want to keep both then re-order the lines in show action
#venus = Venue.all
render :layout => nil
Also, change the venu to #venu in the show.html.erb and if you like correct the typo in the instance variables #venu => #venue :) (Could happen to any of us)
Usually in the index method, it should show all the venus, and in the show method it would show detailed view of each venue.
Try setting something like this:
def index
#venus = Venue.all
end
def show
render :layout => nil
#venue = Venue.find(params[:id])
end
now in show.html.erb you should be able to use
#venue.name
and in your index.html.erb, you can iterate over the venus like so:
<% #venus.each do |v| %>
<%= link_to v do %>
<%= v.name %>
<% end %>
<% end %>
The above answer is correct. You can use #venus = Venue.all in your show view but because you render first it throws you an error. Just render at the end.

Rails 4 form_for nested resources issue

I have researched similar questions however I don't feel link they have addressed my particular issue:
Rails form_for results in POST instead of PUT when trying to edit
form_for with nested resources
I'm a novice with Rails (using Rails 4.2.5) an am attempting my first application. My issue is two fold: (1) When a user goes to edit a user story the fields of the form do not populate with previously inputted data (2) When the form is resubmitted, a new entry is created, opposed to editing the old data.
I have a feeling that my form_for for user_stories/edit.html.erb is the issue. When I take out the .build method from the form I get the following error message:
undefined method `to_key' for #UserStory::ActiveRecord_Associations_CollectionProxy:0x007f456a759138>
The projects/_form.html.erb for my project's view does not have the .build method and functions correctly. However the only way I can get the `user_stories/_form.html.erb form to work is if I attach the build method.
Here is my code:
user_story.rb
class UserStory < ActiveRecord::Base
belongs_to :project
belongs_to :user
include RankedModel
ranks :row_order
end
project.rb
class Project < ActiveRecord::Base
has_many :user_stories
belongs_to :user
end
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :projects do
resources :user_stories
end
end
resources :user_stories do
post :update_row_order, on: :collection
end
root 'welcome#index'
end
user_stories/_form.html.erb
<%= form_for([#project, #user_story.build]) do |f| %>
<div class="form-group">
<p>As a ...</p>
<%= f.text_field :param1, placeholder: "type of user", class: "form-control" %>
</div>
<div class="form-group">
<p>I want ...</p>
<%= f.text_field :param2, placeholder: "desired functionality", class: "form-control" %>
</div>
<div class="form-group">
<p>so that...</p>
<%= f.text_field :param3, placeholder: "reason for desired functionality", class: "form-control" %>
</div>
<div class="actions">
<%= f.submit class: "btn btn-primary" %>
</div>
<% end %>
user_stories_controller.rb
class UserStoriesController < ApplicationController
before_action :set_project
before_action :set_user_story, except: [:create]
def index
#user_story = #project.user_stories.rank(:row_order).all
end
def update_row_order
#user_story.row_order_position = user_story_params[:row_order_position]
#user_story.save
render nothing:true # this is a POST action, updates sent via AJAX, no view rendered
end
def create
#user_story = #project.user_stories.create(user_story_params)
redirect_to #project
end
def new
end
def destroy
if #user_story.destroy
flash[:success] = "User story deleted"
else
flash[:error] = "User story could not be deletd"
end
redirect_to #project
end
def complete
user_story.update_attribute(completed_at, Time.now)
redirect_to #project, notice: "User story completed functionality complete"
end
def update
respond_to do |format|
if #project.user_stories.update(#project, user_story_params)
format.html { redirect_to project_path(#project), notice: 'User story was successfully updated.' }
format.json { render :show, status: :ok, location: #user_story }
else
format.html { render :edit }
format.json { render json: #user_story.errors, status: :unprocessable_entity }
end
end
end
def edit
#project = Project.find(params[:project_id])
#user_story = #project.user_stories(params[:id])
end
def show
end
private
def set_project
#project = Project.find(params[:project_id])
end
def set_user_story
#user_story = #project.user_stories(params[:id])
end
def user_story_params
params[:user_story].permit(:param1, :param2, :param3, :row_order_position)
end
end
There are just a few changes needed (tweaks, really), and I'll go through them top-to-bottom.
1) before_action :set_user_story
This will use the param[:id] to find the proper #user_story model object and automatically make it available to the proper methods. In this case it's being excepted for :create, but should also exclude other methods that don't have an :id in the route. Use this instead:
before_action :set_user_story, except: [:index, :new, :create]
This will solve (or prevent) some annoying and persistent ActiveRecord failures.
2) The index action
In this method, the name of the variable is non-standard by Rails naming conventions. The variable is currently singular, but represents a list of UserAction model object, which typically uses a plural name. Use this, instead:
def index
#user_stories = #project.user_stories.rank(:row_order).all
end
This change will cause a break in the app/views/user_stories/index.html.erb view, where any use of the #user_story variable would need to be changed to #user_stories. Keeping with naming conventions has many immediate and long-term benefits, so it's worth making the extra effort to change this to be consistent.
Note: the index action typically doesn't have a singular model object to work with, as this action is used to provide a list of the model objects.
3) The new action
The new action is used to create and initialize a new model object for editing. As the before_action :set_user_story is no longer being called for the new action, the #user_story model object has to be created here. This code will do that correctly:
def new
#user_story = UserStory.new
#user_story.project = #project
# Set other important default values for display now
end
And at this point, you should be able to successfully create a new UserStory model object, ready to be edited by the user.
4) The edit action
As the before_action :set_user_story handler is already being called for the edit action, there's no need to query for #user_story from within the body of the edit action; that line can be removed:
def edit
#project = Project.find(params[:project_id])
end
This will actually fix the original issue that was reported, as this form of find will (unfortunately for this situation) return multiple records, which means that you get a collection back, and not a single record. This is the actual cause of this error message:
undefined method `to_key' for #UserStory::ActiveRecord_Associations_CollectionProxy:0x007f456a759138>
Assigning the #user_story within the edit action overwrote the value that had previously been assigned from the before_action handler, and replaced it with an improper query result.
5) The complete action
The complete action is a custom member action, which means that it depends on the :id, just like many of the other actions. The code is almost correct, except that the user_story variable used within the body of the method is actually missing the #; this is originally retrieved by the before_action handler.
def complete
#user_story.update_attribute(completed_at, Time.now)
redirect_to #project, notice: "User story completed functionality complete"
end
It's likely that this method had not been called yet during testing, as the edit action was an upstream test that failed. This should work when you get to testing this method.
6) Teh codez
Changing those few details will finalize the UserStoriesController, which was in pretty great shape to begin with. Adding in those changes, this is the final controller code:
class UserStoriesController < ApplicationController
before_action :set_project
before_action :set_user_story, except: [:index, :new, :create]
def index
#user_stories = #project.user_stories.rank(:row_order).all
end
def update_row_order
#user_story.row_order_position = user_story_params[:row_order_position]
#user_story.save
render nothing:true # this is a POST action, updates sent via AJAX, no view rendered
end
def create
#user_story = #project.user_stories.create(user_story_params)
redirect_to #project
end
def new
#user_story = UserStory.new
#user_story.project = #project
# Set other important default values for display now
end
def destroy
if #user_story.destroy
flash[:success] = "User story deleted"
else
flash[:error] = "User story could not be deleted"
end
redirect_to #project
end
def complete
#user_story.update_attribute(completed_at, Time.now)
redirect_to #project, notice: "User story completed functionality complete"
end
def update
respond_to do |format|
if #project.user_stories.update(#project, user_story_params)
format.html { redirect_to project_path(#project), notice: 'User story was successfully updated.' }
format.json { render :show, status: :ok, location: #user_story }
else
format.html { render :edit }
format.json { render json: #user_story.errors, status: :unprocessable_entity }
end
end
end
def edit
#project = Project.find(params[:project_id])
end
def show
end
private
def set_project
#project = Project.find(params[:project_id])
end
def set_user_story
#user_story = #project.user_stories(params[:id])
end
def user_story_params
params[:user_story].permit(:param1, :param2, :param3, :row_order_position)
end
end

ruby on rails recieving error: First argument in form cannot contain nil or be empty

We have an error in our erb files and the screen shot is here
We have everything in games.html.erb as below
<h1>Games#game</h1>
<%= form_for(#game) do |f| %>
<div class="game-field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="actions">
<%= submit_tag "Game Created", :class=> "btn" %>
</div>
<% end %>
and here is the controller
class GamesController < ApplicationController
before_filter :store_location, :only => [:index, :show]
before_filter :require_user, :only => :show
before_filter :assign_game, :only => [:show, :destroy]
def index
#game = Game.new
end
def new
#game = Game.new
end
#game.add_player_from_user(current_user)
respond_to do |format|
format.html
format.json do render :json => {
:shouldStartNewRound => #game.is_ready_for_new_round?,
:shouldPassCards => #game.is_ready_to_pass?,
:isStartingFirstRound => #game.rounds.empty?,
:shouldReloadWaitAutoPlay => #game.should_reload?(current_player),
:shouldReloadAndJustWait => #game.should_reload_and_wait?(current_player)
}
end
end
end
def create
#game = Game.new(params[:game])
api_key = ""
api_secret = ""
#openTok = OpenTok::OpenTokSDK.new api_key, api_secret
session = #openTok.create_session request.remote_addr
#game.update_attributes(:session_id => session.session_id)
name = game[:name] # input from html
#game[:name] = name # setting input into db object (model)
if #game.save
redirect_to #game, alert: "game created"
else
render action: 'new'
end
end
def destory
#game.destory
redirect_to games_url
end
def reload
reload_partial
end
end
We are trying to create a deuces card game and we have bee stuck on that single problem. I have also looked for other similar problem that people have asked and we were not able to find the issue, it says that we have to put .new function to initialize the data. But the error is still showing up. Please help us!
You don't have a #game method in your GameController class.
Rename your #index action into #game or your View file.
I don't know how your routes file looks like but I suppose that the index action is the action that you call if somebody calls the root route, so http://localhost:3000/. If that's right then you have to rename game.html.erb into index.html.erb.
It looks like you are calling the action #game on controller Games. There is no #game method defined on the controller, so the instance variable #game never gets defined like it does in #index or #new. You can create a #game method in the controller and define #game in that method as needed.
Also, you have some code in the controller that needs to be inside of a method, starting at #game.add_player_from_user(current_user). Right now it's just executing at load time and probably not doing anything that makes any sense.
Finally, I noticed that you misspelled 'destroy' in a couple of places - search for 'destory' and replace with 'destroy' to avoid problems later.

Resources