Displaying items captured via a nested form - ruby-on-rails

I'm trying to build a small expense tracking app. Using the nested_form gem to add line items. There is an Expense model which accepts nested attributes. Items belong to expenses and there is a foreign key association. Here's the expense controller:
class ExpensesController < ApplicationController
def new
#expense = Expense.new
#item = #expense.items.build
end
def index
#expenses = Expense.all
##items = Item.where(:expense_id => #expense.id)
end
def show
#expense = Expense.find(params[:id])
#items = Item.where(:expense_id => #expense.id)
end
def create
#expense = Expense.new(expense_params)
respond_to do |format|
if #expense.save
format.html { redirect_to #expense, notice: 'Expense Report Submitted.' }
format.json { render :show, status: :created, location: #expense }
else
format.html { render :new }
format.json { render json: #expense.errors, status: :unprocessable_entity }
end
end
end
def edit
#expense = Expense.find(params[:id])
end
def update
#expense = Expense.find(params[:id])
if #expense.save(expense_params)
flash[:notice] = "Expense Report Updated"
redirect_to #expense
else
render 'edit'
end
end
def destroy
#expense = Expense.find(params[:id])
#expense.destroy
redirect_to 'root'
end
private
def expense_params
params.require(:expense).permit(:department_id, :expense_type_id, :notes, items_attributes: [:id, :description, :amount, :issue_date, :_destroy])
end
end
The form looks like:
<%= nested_form_for (#expense) do |f| %>
<% if #expense.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#expense.errors.count, "error") %> prohibited
this expense from being saved:</h2>
<ul>
<% #expense.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class"row">
<div class="col-md-8">
<div class="form-group">
<%= f.label :department_id %><br>
<%= f.collection_select(:department_id, Department.all, :id, :department_name, prompt: true, class: "dropdown-menu") %>
</div>
<div class="form-group">
<%= f.label :expense_type_id %><br>
<%= f.collection_select(:expense_type_id, ExpenseType.all, :id, :expense_name, prompt: true, class: "form-control") %>
</div>
<%= f.fields_for :items do |i| %>
<div class="form-group">
<%= i.label :description%>
<%= i.text_field :description, class: "form-control" %>
</div>
<div class="form-group">
<%= i.label :amount%>
<%= i.text_field :amount, class: "form-control" %>
</div>
<div class="form-group">
<%= i.label :issue_date%>
<%= i.date_select :issue_date, class: "form-control" %>
</div>
<%= i.link_to_remove "Remove", class: "btn btn-default" %>
<% end %>
<div><p><%= f.link_to_add "Add Expense", :items, class: "btn btn-default" %></p></div>
<div class="form-group">
<%= f.label :notes %>
<%= f.text_area :notes, class: "form-control" %>
</div>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
When I hit submit, the expense and items are saved. I checked with Sqlite browser and the foreign key values are captured for each item.
The index file looks like:
<% #expenses.each do |expense| %>
<tr>
<td><%= expense.item.description %></td>
<td><%= number_to_currency(expense.item.amount) %></td>
<td><%= expense.item.issue_date %></td>
<% end %>
I tried the usual combinations (like expense.item.description) by passing through a block, but they aren't working. I would like to know how to display the expense and the associated items in the show and index pages.

Looking at your new action, in your models you would have
class Expense < ActiveRecord::Base
has_many :items
accepts_nested_attributes_for :items
end
So when you are doing expense.items in your view it gives you an active record relation(since expense has many items), try this:
<% #expenses.each do |expense| %>
<% expense.items.each do |item| %>
<tr>
<td><%= item.description %></td>
<td><%= number_to_currency(item.amount) %></td>
<td><%= item.issue_date %></td>
</tr>
<% end %>
<% end %>

Related

NoMethodError in Products#add , undefined method `errors' for nil:NilClass

i have no idea why this error come out, can anyone help me? thanks
It shown when i comment out the product controller( def add) there.
And also if possible is that can help me check the add.html.erb there is that my option_for_select to get all the outlets correct?
Errors show in website
Errors show in console
ActionView::Template::Error (undefined method `errors' for nil:NilClass
#virtual_path = "shared/_error_messages";object = local_assign
s[:object]; object = object;; if object.errors.any?
^^^^^^^):
1: <% if object.errors.any? %>
2: <div id="error_explanation">
3: <div class="alert alert-danger">
4: The form contains <%= pluralize(object.errors.count, "error")
%>.
app/views/shared/_error_messages.html.erb:1
app/views/products/add.html.erb:5
app/views/products/add.html.erb:4
Add.html.erb
<h1>Add to outlet</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: #product, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :quantity %>
<%= f.number_field :quantity, class: 'form-control' %>
<%= f.label :price %>
<%= f.number_field :price, class: 'form-control' %>
<%= f.label :outlet %>
<%= f.select :outlet, options_for_select(#outlets), :include_blank => true %>
<%= f.hidden_field :category_id, value: 1 %>
<%= f.submit "Save changes", class: "btn btn-primary" %>
<% end %>
_error_messages.html.erb
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Product Controller
class ProductsController < ApplicationController
def category
#category = Category.find(params[:id])
end
def index
#products = Product.all
end
def show
#product = Product.find(params[:id])
end
def new
#product = Product.new
#product.category_id = params[:category_id]
end
def create
#product = Product.new(product_params)
#category_id = Category.find(params[:product] [:category_id])
if #product.save
flash[:success] = "Succesful create!"
redirect_to #product
else
render 'new'
end
end
def outlet
#outlet = Outlet.find(params[:id])
end
def add
##product = Product.find(params[:id])
##outlet = Outlet.find(params[:outlet_id])
end
def update
#product = Product.find(params[:id])
#outlet = Outlet.find(params[:outlet][:name])
if #product.update(product_params)
flash[:success] = "Product updated"
redirect_to #product
else
render 'add'
end
end
private
def product_params
params.require(:product).permit(:name, :quantity, :price,
:category_id)
end
end

How do I use a hidden_field_tag to help create a valid object in Ruby on Rails?

I'm creating a prayer website with the ability to comment on a public prayer. When I try to create a comment on a prayer, it returns four errors:
Prayer must exist
User must exist
User can't be blank
Prayer can't be blank
And then every comment creation form on the page is automatically filled in with the text I put in the first comment box, and all of them have the same 4 errors on them. I tried to use the hidden_field_tag to put in the comment form for the right user id and prayer id but they aren't put into the hash for the new comment object, they are separate.
This is the debug stuff at the bottom of the page:
#<ActionController::Parameters {"authenticity_token"=>"abcdefg", "comment"=>#<ActionController::Parameters {"content"=>"Comment comment 1 2 3"} permitted: false>, "user_id"=>"1", "prayer_id"=>"301", "commit"=>"Comment", "controller"=>"comments", "action"=>"create"} permitted: false>
controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
if logged_in?
#prayer = current_user.prayers.build
#comment = current_user.comments.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
end
controllers/comments_controller.rb
class CommentsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
#comment = Comment.create(comment_params)
if #comment.save
flash[:success] = "Comment created!"
redirect_to root_url
else
#feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home', status: :unprocessable_entity
end
end
def destroy
#comment.destroy
flash[:success] = "Comment deleted"
redirect_back_or_to( root_url, status: :see_other )
end
private
def comment_params
params.require(:comment).permit(:content, :prayer_id, :user_id)
end
def correct_user
#comment = current_user.comments.find_by(id: params[:id])
redirect_to root_url, status: :see_other if #comment.nil?
end
end
models/comment.rb
class Comment < ApplicationRecord
belongs_to :prayer
belongs_to :user
default_scope -> { order( created_at: :desc) }
validates :user_id, presence: true
validates :prayer_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
end
views/comments/_comment.html.erb
<li id="comment-<%= comment.id %>">
<%= link_to gravatar_for(comment.user, size: 30), comment.user %>
<span class="user"><%= link_to comment.user.name, comment.user %></span>
<span class="comment-content"><%= comment.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(comment.created_at) %> ago.
<% if current_user?(comment.user) %>
<%= link_to "delete comment", comment, data: { "turbo-method": :delete,
"turbo-confirm": "Are you sure?"} %>
<% end %>
</span>
</li>
views/prayers/_prayer.html.erb
<li id="prayer-<%= prayer.id %>">
<%= link_to gravatar_for(prayer.user, size: 50), prayer.user %>
<span class="user"><%= link_to prayer.user.name, prayer.user %></span>
<span class="content">
<%= prayer.content %>
<% if prayer.image.attached? %>
<%= image_tag prayer.image.variant(:display) %>
<% end %>
</span>
<span class="timestamp">
Posted <%= time_ago_in_words(prayer.created_at) %> ago.
<% if current_user?(prayer.user) %>
<%= link_to "delete", prayer, data: { "turbo-method": :delete,
"turbo-confirm": "Are you sure?"} %>
<% end %>
</span>
<span>
<%= render 'shared/comment_form', prayer_id: prayer.id %>
</span>
<span>
<% if prayer.comments.any? %>
<ol class="comments">
<% prayer.comments.each do |comment| %>
<%= render comment %>
<% end %>
</ol>
<% end %>
</span>
</li>
** views/shared/_comment_form.html.erb * **
<%= form_with(model: #comment) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<center>
<div class="field">
<%= f.text_area(:content, placeholder: "Comment on this prayer...") %>
</div>
<div><%= hidden_field_tag :user_id, #user.id %></div>
<div><%= hidden_field_tag :prayer_id, prayer_id %></div>
<%= f.submit "Comment", class: "btn btn-primary" %>
</center>
<% end %>
views/shared/_error_messages.html.erb
<% if object != nil && object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
views/shared/_feed.html.erb
<% if #feed_items.any? %>
<ol class="prayers">
<%= render #feed_items %>
</ol>
<%= will_paginate #feed_items,
params: { controller: :static_pages, action: :home } %>
<% end %>
With help from Maxence, I discovered I need to use f.hidden_field versus hidden_field_tag
so I changed the views/shared/_comment_form.html.erb to the following:
<%= form_with(model: #comment) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<center>
<div class="field">
<%= f.text_area(:content, placeholder: "Comment on this prayer...") %>
</div>
<div><%= f.hidden_field :user_id, value: #user.id %></div>
<div><%= f.hidden_field :prayer_id, value: prayer_id %></div>
<%= f.submit "Comment", class: "btn btn-primary" %>
</center>
<% end %>

Ruby on Rails form error not giving any output/ not working

Hello guys I searched everywhere on how to output form errors but no luck. I tried almost all the code I found on the internet but still didn't work.
Here are some of my files:
view/books/new.html.erb
<div class="container">
<h2>Create a new book</h2>
<%= form_with model: #book, url: create_new_book_path do |f| %>
<% if #book.errors.any? %>
<ul>
<% #book.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<div class="form-group row">
<%= label_tag(:title, "Title:", class: "col-sm-2 col-form-label") %>
<div class="col-sm-10">
<%= f.text_field :title, class: "form-control" %>
</div>
</div>
<div class="form-group row">
<%= label_tag(:price, "Price:", class: "col-sm-2 col-form-label") %>
<div class="col-sm-10">
<%= f.text_field :price, class: "form-control" %>
</div>
</div>
<div class="form-group row">
<%= label_tag(:subject_id, "Subject:", class: "col-sm-2 col-form-label") %>
<div class="col-sm-10">
<%= f.collection_select :subject_id, #subjects, :id, :name, class: "form-control" %>
</div>
</div>
<div class="form-group row">
<%= label_tag(:description, "Book description:", class: "col-sm-2 col-form-label") %>
<div class="col-sm-10">
<%= f.text_area :description, class: "form-control" %>
</div>
</div>
<%= submit_tag "Create book", class: "btn btn-success" %>
<%= link_to "Cancel", books_path, class: "btn btn-danger" %>
<% end %>
<br/>
</div>
books_controller.rb
class BooksController < ApplicationController
def index
#books = Book.all
end
def show
#book = Book.find(params[:id])
end
def new
#book = Book.new
#subjects = Subject.all
end
def create
#book = Book.new(book_params)
end
def book_params
params.require(:book).permit(:title, :price, :subject_id, :description)
end
def edit
#book = Book.find(params[:id])
#subjects = Subject.all
end
def update
#book = Book.find(params[:id])
if #book.update_attributes(book_params)
redirect_to action: "index"
else
#subjects = Subject.all
render action: "edit"
end
end
def destroy
Book.find(params[:id]).destroy
redirect_to action: "index"
end
end
routes.rb
get 'books/new', to: 'books#new', as: 'new_book'
post 'books/create', to: 'books#create', as: 'create_new_book'
view/layouts/application.html.erb
<main role="main">
<%= yield %>
</main>
I don't really know what I'm missing to get the errors, grateful for your answers!!
try putting them in a flash like this and render it in your view
def create
#book = Book.new
if #book.update(book_params)
flash[:notice] = 'success'
redirect_to action: 'index'
else
flash[:alert] = #book.errors.full_messages.join(', ')
redirect_to action: 'index'
end
end
and in your view render those flashes like
<% if flash[:notice]%>
<span class='notice'><%= flash[:notice] %></span>
<% end %>
<% if flash[:alert]%>
<span class='alert'><%= flash[:alert] %></span>
<% end %>

Validation errors are not displaying at multipart form in rails

I have a model BookingPost that has many Reservations.
In booking_posts/show.html.erb I have:
<div class="card-action">
<%= form_for([#reservation.booking_post, #reservation], html: {multipart: true}, class: "col s12") do |f| %>
<% if #reservation.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#reservation.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #reservation.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="col s6">
<%= f.label :start %>
<%= f.date_field :start, placeholder: "start time", class: "datepicker" %>
</div>
<div class="col s6">
<%= f.label :end %>
<%= f.date_field :end, placeholder: "end time", class: "datepicker" %>
</div>
<div class="col s6">
<%= f.label :reservation_time %>
<%= f.time_field :reservation_time, placeholder: "time", class: "timepicker", id: "timepicker", type: "time" %>
</div>
<div class="input-field col s6">
<%= f.label :name %>
<%= f.text_field :name, class: "validate" %>
</div>
<div class="input-field col s6">
<%= f.label :email %>
<%= f.text_field :email, class: "validate" %>
</div>
<div class="input-field col s6">
<%= f.label :phone_number %>
<%= f.text_field :phone_number, class: "validate" %>
</div>
<div class="waves-effect waves-light btn">
<%= f.submit t(:submit_reservation)%>
</div>
<% end %>
<br>
</div>
booking_posts_controller:
# GET /booking_posts/1
# GET /booking_posts/1.json
def show
#booking_picture = #booking_post.booking_pictures.build
#booking_pictures = #booking_post.booking_pictures
#reservation = #booking_post.reservations.build
#reservations = #booking_post.reservations
end
reservations_controller:
# GET /reservations/new
def new
#reservation = Reservation.new
end
def create
#booking_post = BookingPost.find(params[:booking_post_id])
#email= User.where(admin: true).first.email
#reservation = #booking_post.reservations.build(reservation_params)
respond_to do |format|
if #reservation.save
#saved_reservation = #reservation
format.html { redirect_to :back, notice: 'Reservation was successfully created.' }
format.json { render :show, status: :created, location: #reservation }
ReservationMailer.fresh_message(#saved_reservation, #email).deliver_now
else
format.html {redirect_to :back
flash[:info] = #reservation.errors.full_messages do |m|
m
end}
format.json { render json: #reservation.errors, status: :unprocessable_entity }
end
end
end
But I can't see any validation errors, even like presence: true.
Seems like I don't have correct #reservation, because I have a deal with Array in my form_for. Help me please to solve this issue, thanks!
The reason you are not seeing validation errors is because you are issuing a redirect_to :back when validations fails instead of rendering :new action which already includes any errors you might have inside the #reservation instance object.
Also you were trying to add the error messages into the flash message while trying to display them using the #reservation object.
this should be the correct else clause
else
format.html { render :new }
format.json { render json: #reservation.errors, status: :unprocessable_entity }
end

Rails join collection not recognizing parameters

I'm trying to create a record in a join table named Interventions. Basically in my application a user can do many interventions on an incident, and an incident can have interventions by many users. I pass the needed strong parameters, but the application gives the following errors when I try to save: "Incident must exist" and "User must exist". I spent hours on this, and can't figure out what is the problem. Can you please help me? I post the relevant code here:
user.rb (model)
has_many :interventions
has_many :incidents, through: :interventions
incident.rb (model)
has_many :interventions
has_many :users, through: :interventions
intervention.rb (model)
belongs_to :incident_priority
belongs_to :intervention_status
interventions_controller.rb
def new
#incident = Incident.find(params[:incident])
#user = User.find(current_user.id)
#intervention = Intervention.new(:user_id => #user, :incident_id => #incident)
#project = #incident.channel.project
#mirth = Mirth.find_by server_id: #incident.mirth_server_id
end
def create
#incident = Incident.find(params[:incident_id])
#user = User.find(params[:user_id])
#intervention = Intervention.create(intervention_params)
#project = #incident.channel.project
#mirth = Mirth.find_by server_id: #incident.mirth_server_id
respond_to do |format|
if #intervention.save
format.html { redirect_to new_intervention_path(#incident), notice: 'Intervention was successfully created.' }
format.json { render :show, status: :created, location: #intervention }
else
format.html { render :new, incident: :incident_id }
format.json { render json: #intervention.errors, status: :unprocessable_entity }
end
end
end
< .... >
def intervention_params
params.require(:intervention).permit(:user_id, :incident_id, :incident_priority_id, :begin_date, :end_date, :description,
:intervention_status_id, :forwarded_to)
end
In my view (interventions_form.html.erb):
<%= form_for(#intervention) do |f| %>
<% if #intervention.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#intervention.errors.count, "error") %> prohibited this intervention from being saved:</h2>
<ul>
<% #intervention.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group form-inline">
<%= hidden_field_tag :user_id, #user.id %>
<%= hidden_field_tag :incident_id, #incident.id %>
<strong>Interveniente:</strong>
<%= #user.first_name %> <%= #user.last_name %>
</div>
<div class="form-group form-inline">
<%= f.label 'Prioridade' %>
<%= f.collection_select :incident_priority_id, IncidentPriority.all, :id, :description, {}, {class: "form-control"} %>
</div>
<div class="form-group form-inline">
<%= f.label 'Data início intervenção' %>
<%= f.datetime_select :begin_date %>
</div>
<div class="form-group form-inline">
<%= f.label 'Data fim intervenção' %>
<%= f.datetime_select :end_date, :include_blank => true %>
</div>
<div class="form-group form-inline">
<%= f.label 'Observações' %>
<%= f.text_area :description %>
</div>
<div class="form-group form-inline">
<%= f.label 'Estado' %>
<%= f.collection_select :intervention_status_id, InterventionStatus.all, :id, :description, {}, {class: "form-control"} %>
</div>
<div class="form-group form-inline">
<%= f.label 'Encaminhado para:' %>
<%= f.text_area :forwarded_to %>
</div>
<div class="actions" align="right">
<%= link_to 'Voltar', incidents_path(:mirth => #mirth, :project => #project), class: "btn btn-info" %>
<%= f.submit "Gravar", class: "btn btn-info" %>
</div>
I run debug and the values in the hidden_field_tags are correctly filled. Also in the controller the #user and #incident are correctly populated, but #intervention has nil in the foreign keys :user_id and :incident_id.
Thanks in advance!
You need to associate the user and the incident with the intervention.
in your model...
class Intervention
belongs_to :user
belongs_to :incident
in your create method...
def create
#incident = Incident.find(params[:incident_id])
#user = User.find(params[:user_id])
#intervention = Intervention.create(intervention_params)
#intervention.incident = #incident
#intervention.user = #user
...
You need to change this:
def create
#incident = Incident.find(params[:incident_id])
#user = User.find(params[:user_id])
# ...
end
For this:
def create
#incident = Incident.find(params[:incident_id])
#user = User.find(params[:user_id])
intervention_params.merge(user_id: #user.id, incident_id: #incident.id)
# ...
end
And your model:
class Intervention
belongs_to :user
belongs_to :incident

Resources