This helped for display on an individual 'items' show page
Accessing an attribute of a linked model in Rails
However I'm having trouble doing the same for an 'all items table'
...
<% #items.each do |item| %>
...
<td><%= item.room.name %></td>
...
Clearly where one room has many items.
only this works:
<td><%= item.room_id %></td>
I can't seem to use it there, gives:
undefined method `name' for nil:NilClass
Have a look at Rails' Delegate module:
class Item < ActiveRecord::Base
# ...
delegate :name, :to => :room, :allow_nil => true, :prefix => :room
end
This will add the instance method room_name to Item, which will fail more gracefully (returning nil if there is no room, instead of the NilClass error).
Related
I have a Plan model that has_many Versions. I'm nesting Version inputs inside a Plan form, and even though the validations seem to be working, the form inputs don't display any errors.
I don't know if this is relevant, but because the nested inputs are scattered around the form, I open the nested input blocks with simple_fields_for :versions_attributes[0] instead of simple_fields_for :versions because I want to be specific that all the inputs around the form correspond to the same object. Otherwise the hash would be built with a different key for each different block (eg: versions_attributes: { 0: {amount: value}, 1: {another_field: another_value} } instead of versions_attributes: { 0: {amount: value, another_field: another_value}}.
plan.rb:
class Plan < ApplicationRecord
has_many :versions
accepts_nested_attributes_for :versions
validates_associated :versions
end
version.rb
class Version < ApplicationRecord
belongs_to :plan
validates :amount, presence: true
end
plans_controller.rb
class PlansController < ApplicationController
def new
#plan = current_account.plans.build
#version = #plan.versions.build
end
def create
#plan = current_account.plans.build(plan_params)
if #plan.save
redirect_to plan_path(#plan)
else
#plan = #base_plan.plans[0]
render :new
end
end
def plan_params
params[:plan][:versions_attributes]["0"]&.merge!(
account_id: current_account.id,
)
params.require(:plan).permit(
:name,
:short_description,
versions_attributes: %i[id account_id amount],
)
end
end
form.html.erb:
<%= simple_form_for [#plan] do |f| %>
<%= f.input :name %>
<%= f.input :short_description %>
<%= f.simple_fields_for "versions_attributes[0]" do |v| %>
<%= v.input :amount %>
<% end %>
<% end %>
Problem:
#version.errors contains a hash with the object's validation errors. However, the related inputs don't render validation errors nor are the form-group-invalid has-error CSS classes added (provided by the simple_form gem).
My first guess is that it's got something to do with the create action. My second guess is that it's got something to do with the way I'm opening the nested input's blocks (described above).
Either way, I'm confused because #version.errors contains the nested object's errors.
Im building a basic chat room website with multiple chat rooms that users can comment on.
I managed to get my messages/comments to show up on each Room.show page, however when I go to enter
<p><%= message.user.name if message.user %> <small><em><%= "#{time_ago_in_words(message.created_at)} ago" %></em></small></p>
I receive a nil can't be converted to a Time value error. Also, I cannot get the Users name to show up. It just registers blank.
Here is the section of my Room.show.html.erb that is registering an error
<% #message.each do |message| %>
<div class="row">
<p><%= message.user.name if message.user %> <small><em><%= "#{time_ago_in_words(message.created_at)} ago" %></em></small></p>
<p><%= message.body %></p>
</div>
<% end %>
Here is my Message controller
def show
#rooms = Room.all
#message = #room.message
end
Here is my Routes file
Rails.application.routes.draw do
devise_for :users
#resources :messages
resources :users
resources :rooms do
resources :messages
end
root 'rooms#index'
end
Message.rb
class Message < ApplicationRecord
belongs_to :user
belongs_to :room
end
And room.rb
class Room < ApplicationRecord
has_many :message
end
Using updated version of rails.
message.created_at is nil. You can verify it by changing your code slightly:
<%= "#{time_ago_in_words(message.created_at || Time.now)} ago" %>
This will always show less than a minute ago because message.created_at is nil.
<%= "#{time_ago_in_words(message.created_at)} ago" if message.created_at %>
This will not show anything because message.created_at is nil.
I suggest that you use a debugging library like pry to help you troubleshoot this. You could do something like this:
<% #message.each do |message| %>
<div class="row">
<% binding.pry unless message && message.created_at %>
<p><%= message.user.name if message.user %> <small><em><%= "#{time_ago_in_words(message.created_at)} ago" %></em></small></p>
<p><%= message.body %></p>
</div>
<% end %>
That will set a breakpoint that will be triggered if message or message.created_at are nil, and allow you to use the Rails console to inspect the variables to help narrow down the problem.
Like others mentioned, you will need to make sure your models are appropriately constructed:
class Room < ApplicationRecord
has_many :messages
end
You should also check your schema.rb to ensure that the Message model has the following:
t.datetime "created_at", null: false
t.index ["room_id"], name: "index_messages_on_room_id"
The first is needed because without it message.created_at will not exist at all, and the second is needed for the association between the two models.
Your controller code doesn't make any sense and I'm assuming that you manually typed it into your question rather than copying and pasting it. You define #rooms as an ActiveRecord::Relation collection object, but you call #room.message. If you meant #rooms.message then that won't work because you're trying to call an instance method on a collection. I'm not sure what you meant here because the code doesn't make sense.
Additionally, you're not implementing a clean CRUD solution. Your Messages controller's show method should be for rendering a single Message object, not for rendering a collection of rooms' messages.
In general there are many problems with the code, the structure, and the example you have typed out. At the very least though, you must make sure that message.created_at is not nil before attempting to call time_ago_in_words, and that all traces back to:
making sure your models are correctly formed and associated with each other
making sure your controller is fetching real objects
making sure your view is appropriately iterating over those real objects
I am fairly new to ruby of rails, but I would like to build this table from my MySQL database, consisting of two tables, Parents and Children.
enter code here
Children (:id, :name, :parentId)
Parent (:id, :name, :childId)
Here is the HTML code:
<% #children.each do |child| %>
<tr>
<td><%= link_to child.name, child_path(child) %></td>
<td><%= child.ticker %></td>
<td><%= link_to child.parent, parent_path(parent) %></td>
</tr>
<% end %>
The table would be able to link to the show path of both tables, but only when child.parentId = parent.id. The table is on my table page with controller
class StaticPagesController < ApplicationController
def table
#children = Child.all.order('name ASC')
if params(:id) == child.parentId
#parent = Parent.find(params[id])
else
#parent = NULL
end
end
end
I know this is incorrect, but I am struggling to create this table. Any feedback or references are helpful!
Thank you in advance.
If you are using sqlite3, then the database for your application should already be under db/development.sqlite3.
I'm not 100% sure what you're trying to achieve here, but perhaps I can help anyway. If I get it wrong, please feel free to correct me in the comments.
First, your Parent table does not need the childId column that I can see. Rails will still be able to tell which parents have which children, and which children belong to which parents, without it. I'd suggest getting rid of it.
As for the Child table, consider replacing parentId with parent_id. Rails uses the latter by default, and it's generally easier to just go with the default unless you have a good reason.
With these considerations in mind, your migrations for these two models would probably look something like so:
db/migrations/[timestamp]_create_parents.rb
class CreateParents < ActiveRecord::Migration
def change
create_table :parents do |t|
# the id column is created automatically.
t.string :name
end
end
end
db/migrations/[timestamp]_create_children.rb
class CreateChildren < ActiveRecord::Migration
def change
create_table :children do |t|
# this creates a column `parent_id` with a foreign key for enforcing
# validity at the database level and an index for faster searching.
t.references :parent, foreign_key: true, index: true
t.string :name
end
end
end
You will want to associate these two models so that Rails can find the records that go together.
app/models/parent.rb
class Parent < ActiveRecord::Base
...
has_many :children
...
end
app/models/child.rb
class Child < ActiveRecord::Base
...
belongs_to :parent
...
end
Now, I'm not quite sure what you mean to do in your controller action. To start, you could just write:
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def table
#children = Child.all.order('name ASC')
end
end
Make sure you have a route for this controller action. Then your view (slightly corrected below) ought to work, except perhaps for this ticker attribute. Is that something you have defined in your database table?
app/views/static_pages/table.html.erb
<table>
<% #children.each do |child| %>
<tr>
<td><%= link_to child.name, child_path(child) %></td>
<td><%= child.ticker %></td> // as long as this attribute is defined, I believe it should work.
<td><%= link_to child.parent.name, parent_path(child.parent) %></td>
</tr>
<% end %>
</table>
This is an error I can not seem to figure out I believe I have it routed. This is the error
No route matches {:action=>"ticket_action", :controller=>"tickets"}
I get this error after this code
<h4>New Action</h4>
<% form_tag :action => 'ticket_action' do %>
<p><b>Description</b><br/>
<%= text_area 'description', 'description', 'rows' => 5 %><br/>
User: <%= select("actUser", "user_id", User.find(:all).collect{|u| [u.name, u.id] } )%>
<% end %>
I have this on my ticket_controller.rb is that the proper placement for that
#action
def ticket_action
#act = Action.new(
"ticket_id" => flash[:ticket_id],
"description" => params[:description]['description'],
"user_id" => params[:actUser]['user_id']
)
routes
actions GET /actions(.:format) actions#index
POST /actions(.:format) actions#create
new_action GET /actions/new(.:format) actions#new
edit_action GET /actions/:id/edit(.:format) actions#edit
action GET /actions/:id(.:format) actions#show
PUT /actions/:id(.:format) actions#update
DELETE /actions/:id(.:format) actions#destroy
tickets GET /tickets(.:format) tickets#index
POST /tickets(.:format) tickets#create
new_ticket GET /tickets/new(.:format) tickets#new
edit_ticket GET /tickets/:id/edit(.:format) tickets#edit
ticket GET /tickets/:id(.:format) tickets#show
PUT /tickets/:id(.:format) tickets#update
DELETE /tickets/:id(.:format) tickets#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
clients GET /clients(.:format) clients#index
POST /clients(.:format) clients#create
new_client GET /clients/new(.:format) clients#new
edit_client GET /clients/:id/edit(.:format) clients#edit
client GET /clients/:id(.:format) clients#show
PUT /clients/:id(.:format) clients#update
DELETE /clients/:id(.:format) clients#destroy
It would be helpful to post the route to debug this problem, your route may refer to tickets yet your class is ticket.
You should look into restful routes, especially given your use case. It seems you should really have an actions controller (ActionsController, named controllers/actions_controller.rb) and then post to the create action and provide a restful route (resources :actions)
My suggestion would be to read up on rest and rails first.
Additionally the flash isn't where you should store your ticket_id, ideally you should retrieve it in your actions controller's create action by posting to /action/ticket_action/1 and retrieving the id by accessing params[:id] in the controller. If you really must, store it in the session (session[:ticket_id] = "1") but 'rest' is where you should be headed. The flash will be removed and should only be set in the controller and then displayed on the next page, it will be deleted thereafter.
Update: ok thanks for posting your routes.
You can add the missing route like this if you want:
resources :tickets do
member do
post 'ticket_action'
end
end
But it would be better to follow this pattern:
In actions controller:
def new
#action = Action.new
end
Your form should look a bit like this, Rails will know to post to actions#create because #action is a new record (you can check #action.new_record? if you want)
<%= form_for #action do |f| %>
<%= f.text_area :description, :rows => 5 %>
<%= f.hidden_field :ticket_id, flash[:ticket_id] %>
<%= f.select :user_id, User.find(:all).collect{|u| [u.name, u.id] } %>
<%= f.submit "Create" %>
<% end %>
Then in your actions controller:
def create
#action = Action.new(params[:action])
end
or with less magic:
def create
#action = Action.new(:user_id => params[:action][:user_id],
:description => params[:action][:description],
:ticket_id => params[:action][:ticket_id])
if #action.save
redirect_to actions_path(#action, :notice => "Created action")
else
render :new # any errors will be in #action.errors
end
end
You should really be setting the ticket_id in the actions controller's new method though.
def new
#action = Action.new(:ticket_id => params[:ticket_id])
end
And then in your form:
<%= f.hidden_field :ticket_id %>
Your file name should be "tickets_controller.rb", plural.
is it possible to link to a method inside a different model? To execute on button press, or is there another way of doing this, something like an action in a controller?
Method inside staff model:
def clearleave
self.where("grade = '1'").update_all(:leave_balance => 22)
self.where("grade = '2'").update_all(:leave_balance => 25)
self.where("grade = '3'").update_all(:leave_balance => 30)
self.where("grade = '4'").update_all(:leave_balance => 35)
end
inside a view for a different model:
<%=button_to "Clear absences", {:controller => :staffs, :action => :clearleave} %>
Rails bases on the Model View Controller pattern (MVC). This means, that requests (e.g. trough the browser) are handled by your controllers. Controllers will collect the required data from the models and pass it to the views for display.
What you are probably aiming for is something like this:
Routes:
resources :staff do
member do
post :clearleave
end
end
Controller:
# StaffController
def clearleave
#staff = Staff.find(params[:id])
#staff.clearleave # this calls the method in your model
# here you could redirect to e.g. the show page for your staff
# redirect_to staff_path(#staff), :notice => "Cleared successfully"
end
Your button in the view would be:
<%= button_to "Clear absences", clearleave_staff_path(#staff) %>
<!-- not sure if a ", :method => :post" is required here as well -->
<!-- in rails 3 a link_to should also work -->
<%= link_to "Clear absences", clearleave_staff_path(#staff), :method => :post %>