How to Order Nested Attribute according to its Date? - ruby-on-rails

How can we DESC order results according to its :date_value in the quantifieds index?
Results being the nested attribute to quantifieds.
Right now the order is according to where the User added the result in the form, regardless of :date_value.
This has proven more difficult than I would have guessed.
class QuantifiedsController < ApplicationController
before_action :set_quantified, only: [:show, :edit, :update, :destroy]
before_action :logged_in_user, only: [:create, :destroy]
def index
if params[:tag]
#quantifieds = Quantified.tagged_with(params[:tag])
else
#quantifieds = Quantified.joins(:results).all
#averaged_quantifieds = current_user.quantifieds.averaged
#instance_quantifieds = current_user.quantifieds.instance
end
end
def show
end
def new
#quantified = current_user.quantifieds.build
end
def edit
end
def create
#quantified = current_user.quantifieds.build(quantified_params)
if #quantified.save
redirect_to quantifieds_url, notice: 'Quantified was successfully created'
else
#feed_items = []
render 'pages/home'
end
end
def update
if #quantified.update(quantified_params)
redirect_to quantifieds_url, notice: 'Goal was successfully updated'
else
render action: 'edit'
end
end
def destroy
#quantified.destroy
redirect_to quantifieds_url
end
private
def set_quantified
#quantified = Quantified.find(params[:id])
end
def correct_user
#quantified = current_user.quantifieds.find_by(id: params[:id])
redirect_to quantifieds_path, notice: "Not authorized to edit this goal" if #quantified.nil?
end
def quantified_params
params.require(:quantified).permit(:categories, :metric, :result, :date, :tag_list, results_attributes: [:id, :result_value, :date_value, :_destroy])
end
end
class Quantified < ActiveRecord::Base
belongs_to :user
has_many :results #correct
accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true #correct
scope :averaged, -> { where(categories: 'Averaged') }
scope :instance, -> { where(categories: 'Instance') }
validates :categories, :metric, presence: true
acts_as_taggable
CATEGORIES = ['Averaged', 'Instance']
end
class Result < ActiveRecord::Base
belongs_to :user
belongs_to :quantified
end
class CreateQuantifieds < ActiveRecord::Migration
def change
create_table :quantifieds do |t|
t.string :categories
t.string :metric
t.references :user, index: true
t.timestamps null: false
end
add_foreign_key :quantifieds, :users
add_index :quantifieds, [:user_id, :created_at]
end
end
class CreateResults < ActiveRecord::Migration
def change
create_table :results do |t|
t.string :result_value
t.date :date_value
t.integer :quantified_id
t.timestamps null: false
end
end
end
form
<%= javascript_include_tag "quantified.js" %>
<%= simple_form_for(#quantified) do |f| %>
<%= f.error_notification %>
<div class="america">
<form>
<% Quantified::CATEGORIES.each do |c| %>
<%= f.radio_button(:categories, c, :class => "date-format-switcher") %>
<%= label(c, c) %>
<% end %>
<br/>
<br/>
<div class="form-group">
<%= f.text_field :tag_list, quantified: #quantified.tag_list.to_s.titleize, class: 'form-control', placeholder: 'Enter Action' %>
</div>
<div class="form-group">
<%= f.text_field :metric, class: 'form-control', placeholder: 'Enter Metric' %>
</div>
<div id="results">
<%= f.fields_for :results do |result| %>
<%= render 'result_fields', :f => result %>
<% end %>
</div>
<div class="links">
<b><%= link_to_add_association 'Add Result', f, :results %></b>
</div>
<div class="america2">
<%= button_tag(type: 'submit', class: "btn") do %>
<span class="glyphicon glyphicon-plus"></span>
<% end %>
<%= link_to quantifieds_path, class: 'btn' do %>
<span class="glyphicon glyphicon-chevron-left"></span>
<% end %>
<%= link_to #quantified, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn' do %>
<span class="glyphicon glyphicon-trash"></span>
<% end %>
</div>
</form>
</div>
<% end %>
index
<!-- Default bootstrap panel contents -->
<div id="valuations" class="panel panel-default">
<div class="panel-heading"><h4><b>AVERAGE</b></h4></div>
<% #averaged_quantifieds.each do |averaged| %>
<div class="attempt">
<b><%= raw averaged.tag_list.map { |t| link_to t.titleize, tagquantifieds_path(t) }.join(', ') %>
<%= link_to edit_quantified_path(averaged) do %>
(<%= averaged.metric %>)</b>
<% end %>
<ul>
<% averaged.results.each do |result| %>
<li>
<b><%= result.result_value %></b>
<%= result.date_value.strftime("%b %Y") %>
</li>
<% end %>
</ul>
</div>
<% end %>
</div>
<div class="valuations-button">
<%= link_to new_quantified_path, class: 'btn' do %>
<b><span class="glyphicon glyphicon-plus"</span></b>
<% end %>
</div>
<br>
<!-- Default bootstrap panel contents -->
<div id="valuations" class="panel panel-default">
<div class="panel-heading"><h4><b>INSTANCE</b></h4></div>
<% #instance_quantifieds.each do |instance| %>
<div class="attempt">
<b><%= raw instance.tag_list.map { |t| link_to t.titleize, tagquantifieds_path(t) }.join(', ') %>
<%= link_to edit_quantified_path(instance) do %>
(<%= instance.metric %>)</b>
<% end %>
<ul>
<% instance.results.each do |result| %>
<li>
<%= result.date_value.strftime("%b.%d.%y") %>
<%= result.result_value %>
</li>
<% end %>
</ul>
</div>
<% end %>
</div>
<div class="valuations-button">
<%= link_to new_quantified_path, class: 'btn' do %>
<b><span class="glyphicon glyphicon-plus"</span></b>
<% end %>
</div>
Thanks so much for your time!

Got it! Add default_scope { order('date_value DESC') } in result.rb

Related

How to display image in index view

I am using carrierwave and trying to display images of products in the index view. This are my models, controllers and views
product.rb
class Product < ActiveRecord::Base
has_many :order_items
belongs_to :category, required: false
has_many :product_attachments
accepts_nested_attributes_for :product_attachments
mount_uploader :image, ImageUploader
default_scope { where(active: true) }
end
product_attachment.rb
class ProductAttachment < ApplicationRecord
mount_uploader :image, ImageUploader
belongs_to :product
end
products_controller.rb (extract)
class ProductsController < ApplicationController
def index
#products = Product.all
#order_item = current_order.order_items.new
end
def show
#product = Product.find(params[:id])
#product_attachments = #product.product_attachments.all
end
def new
#product = Product.new
#product_attachment = #product.product_attachments.build
#categories = Category.all.map{|c| [ c.name, c.id ] }
end
def create
#product = Product.new(product_params)
#product.category_id = params[:category_id]
respond_to do |format|
if #product.save
params[:product_attachments]['image'].each do |a|
#product_attachment = #product.product_attachments.create!(:image => a, :product_id => #product.id)
end
format.html { redirect_to #product, notice: 'Product was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
private
def product_params
params.require(:product).permit(:name,:price, :active, :description, product_attachments_attributes:
[:id, :product_id, :image], category_attributes: [:category_id, :category])
end
end
index.html.erb
<div class="row">
<div class="col-xs-offset-1 ">
<% #products.each do |product| %>
<%= render "product_row", product: product, order_item: #order_item %>
<% end %>
</div>
_product_row.html.erb
<div class="well">
<div class="row">
<div class="container-fluid row">
<div class="col-md-5 col-lg-5 col-sm-5 col-xs-5 binder">
<br><%= image_tag product.image_url.to_s %><br><br>
</div>
<div class="col-md-4 col-lg-4 col-sm-4 col-xs-4 binder">
<h4 class="text-left"><%= product.name.split.map(&:capitalize).join(' ') %> </h4>
<h4 class="text-left"><span style="color: green"><%= number_to_currency(product.price, :unit => "€") %></span></h4>
<h4><%= link_to Category.find(product.category_id).name, category_path(product.category_id) %></h4>
<h6 class="text-left"><%= link_to 'Delete', product_path(product), method: :delete,
data: { confirm: 'Are you sure?' } %></h6><br><br>
</div>
<div class="col-md-3 col-lg-3 col-sm-3 col-xs-3 binder">
<%= form_for order_item, remote: true do |f| %>
<div class="input-group">
<%= f.number_field :quantity, value: 1, class: "form-control", min: 1 %>
<div class="input-group-btn">
<%= f.hidden_field :product_id, value: product.id %>
<%= f.submit "Add to Cart", class: "btn btn-primary text-right" %>
</div>
</div>
<% end %>
</div>
</div>
</div>
</div>
With <%= image_tag product.image_url.to_s %> the image doesn't appear. When I change it to <%= image_tag product_attachments.first.image_url.to_s %> I get the following error:
undefined local variable or method `product_attachments' for #<#<Class:0x00007f28544ccc68>:0x00007f285dd3aab8>
I am pretty new to Ruby and don't know what I am doing wrong or how to fix this. Any help would be appreciated. I am using Ruby version 2.5.1 and rails 5.2.0 on ubuntu.
I would expect that the following works:
<%= image_tag product.product_attachments.first.image_url.to_s %>
Try this:
image_tag(product.product_attachments.first.image.url.to_s)
It should work. I realized that sometimes image_url doesn't work as expected but image.url does.
If i understand well your snippets, the model you mount the ImageUploader is ProductAttachment (which have the attribute image) so you can remove mount_uploader :image, ImageUploader of your Product model.
The image is mounted on every product_attachments for one product. Just display the images inside the partial by iterating through product_attachments:
<% product.product_attachments.each do |attachment| %>
<%= image_tag(attachment.image.url) %>
<% end %>

Rails: How to compare times and display error?

I'd like to check from and to times.
If from > to, I'd like display an error.
How can I edit my code?
Althogh I tried some codes with cover, include, I haven't be able to apply them to my code.
schema.rb
...
create_table "events", force: :cascade do |t|
t.time "from"
t.time "to"
...
schedules_controller.rb
...
def new
#schedule = Schedule.new
room = #schedule.rooms.build
schedule.events.build
end
def create
#schedule = current_user.schedules.build(schedule_params)
if #schedule.save
flash[:success] = "schedule created!"
redirect_to root_url
else
render 'new'
end
end
def edit
#day_max = Room.where("schedule_id = ?", #schedule.id).maximum(:day)
end
def update
#schedule.rooms.maximum(:day)
if #schedule.update(schedule_params)
flash[:success] = "schedule updated!"
redirect_to root_url
else
render 'edit'
end
end
_schedule_form.html.erb
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
<br>
<%= f.label :departure_date %>
<div class="input-group date" id="datetimepicker">
<%= f.text_field :departure_date, :value => (f.object.departure_date.strftime('%b/%d/%Y') if f.object.departure_date), class: 'form-control' %>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
<script type="text/javascript">
$(function () {
$('#datetimepicker').datetimepicker({format:'MMM-DD-YYYY'});
});
</script>
<br>
<div id="room">
<%= f.simple_fields_for :rooms do |a| %>
<div id="room_<%= a.object.object_id %>">
<p class="day-number-element-selector"><b>Day <%= a.index.to_i + 1 %></b></p>
<%= a.simple_fields_for :events do |e| %>
<span class="form-inline">
<p>
<%= e.input :from, label: false %>
<%= e.input :to, label: false %>
</p>
</span>
<%= e.input :title, label: false %>
<% end %>
</div>
<%= a.link_to_add "Add event", :events, data: {target: "#room_#{a.object.object_id}"}, class: "btn btn-primary" %>
<%= a.input :room %>
<% end %>
</div>
It would be appreciated if you could give me how to check and display error.
You probably want to implement a validator in your event model, as explained in the documentation.
class Event < ActiveRecord::Base
validates :to, presence: true
validates :from, presence: true
validate do |e|
if e.from.present? && e.to.present? and e.from > e.to
e.errors[:base] << "To time must be after from time"
end
end
end

How to stop double rendering in sidebar?

The code is double rendering like so:
when it should just list it out once like so:
Ran 1 miles Apr
Journal 1 days Apr
views/layouts/_stats.html.erb
<% #averaged_quantifieds.each do |averaged| %>
<% averaged.results.each do |result| %>
<div class="<%= result.good? ? 'green' : 'red' %>">
<li>
<%= raw averaged.tag_list.map { |t| link_to t.titleize, tag_path(t) }.join(', ') %><%= link_to edit_quantified_path(averaged) do %> <%= averaged.results.first.result_value %> <%= averaged.metric %> <span class="<%= date_value_label_class(result) %>"> <%= averaged.results.first.date_value.strftime("%b") %></span><% end %>
</li>
</div>
<% end %>
<% end %>
application_controller
def set_stats
#averaged_quantifieds = current_user.quantifieds.averaged if current_user
#instance_quantifieds = current_user.quantifieds.instance if current_user
end
Results are a nested_attribute to Quantifieds.
quantifieds_controller
class QuantifiedsController < ApplicationController
before_action :set_quantified, only: [:show, :edit, :update, :destroy]
before_action :logged_in_user, only: [:create, :destroy]
def index
if params[:tag]
#quantifieds = Quantified.tagged_with(params[:tag])
else
#quantifieds = Quantified.joins(:results).all
#averaged_quantifieds = current_user.quantifieds.averaged
#instance_quantifieds = current_user.quantifieds.instance
end
end
def show
end
def new
#quantified = current_user.quantifieds.build
end
def edit
end
def create
#quantified = current_user.quantifieds.build(quantified_params)
if #quantified.save
redirect_to quantifieds_url, notice: 'Quantified was successfully created'
else
#feed_items = []
render 'pages/home'
end
end
def update
if #quantified.update(quantified_params)
redirect_to quantifieds_url, notice: 'Goal was successfully updated'
else
render action: 'edit'
end
end
def destroy
#quantified.destroy
redirect_to quantifieds_url
end
private
def set_quantified
#quantified = Quantified.find(params[:id])
end
def correct_user
#quantified = current_user.quantifieds.find_by(id: params[:id])
redirect_to quantifieds_path, notice: "Not authorized to edit this goal" if #quantified.nil?
end
def quantified_params
params.require(:quantified).permit(:categories, :metric, :date, :comment, :private_submit, :tag_list, results_attributes: [:id, :result_value, :date_value, :good, :_destroy])
end
end
quantified.rb
class Quantified < ActiveRecord::Base
belongs_to :user
has_many :results #correct
has_many :comments, as: :commentable
accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true #correct
scope :averaged, -> { where(categories: 'Averaged') }
scope :instance, -> { where(categories: 'Instance') }
scope :private_submit, -> { where(private_submit: true) }
scope :public_submit, -> { where(private_submit: false) }
validates :categories, :metric, presence: true
acts_as_taggable
CATEGORIES = ['Averaged', 'Instance']
end
result.rb
class Result < ActiveRecord::Base
belongs_to :user
belongs_to :quantified
has_many :comments, as: :commentable
default_scope { order('date_value DESC') }
scope :good, -> { where(good: true) }
scope :good_count, -> { good.count }
end
The issue came about when trying to introduce differing font colors for results. To make that possible I had to introduce these two lines which are causing the double rendering: <% averaged.results.each do |result| %> <div class="<%= result.good? ? 'green' : 'red' %>">
Thank you for your time and expertise.
You should explain more about the logic and the data stored.
A guess is that there's two records in #averaged_quantifieds and three in averaged.results. It is hard to determine the desired results without knowing the data stored.
Note that you are only displaying the firsts records results (averaged.results.first) in the <%= raw ... line
Try
<% #averaged_quantifieds.each do |averaged| %>
<div class="<%= averaged.results.first.good? ? 'green' : 'red' %>">
<li>
<%= raw averaged.tag_list.map { |t| link_to t.titleize, tag_path(t) }.join(', ') %><%= link_to edit_quantified_path(averaged) do %> <%= averaged.results.first.result_value %> <%= averaged.metric %> <span class="<%= date_value_label_class(result) %>"> <%= averaged.results.first.date_value.strftime("%b") %></span><% end %>
</li>
</div>
<% end %>
Edit: My bad, I missed one result object (<%= date_value_label_class(result) %>) hiding in the rest of the line
Change result to averaged.results.first wherever applicable
<% #averaged_quantifieds.each do |averaged| %>
<div class="<%= averaged.results.first.good? ? 'green' : 'red' %>">
<li>
<%= raw averaged.tag_list.map { |t| link_to t.titleize, tag_path(t) }.join(', ') %><%= link_to edit_quantified_path(averaged) do %> <%= averaged.results.first.result_value %> <%= averaged.metric %> <span class="<%= date_value_label_class(averaged.results.first) %>"> <%= averaged.results.first.date_value.strftime("%b") %></span><% end %>
</li>
</div>
<% end %>

How to fix level.rb to work with :committed days?

Originally I had :committed days working beautifully, but upon changing up the models a bit in order to accommodate the User's ability to check off if he missed a :committed day I now get an error message for the code relating to :committed:
undefined method to_date for nil:NilClass
Line #30 n_days = ((date_started.to_date)..Date.today).count { |date| committed_wdays.include? date.wday }
This code comes from the habit model:
class Habit < ActiveRecord::Base
belongs_to :user
has_many :levels
has_many :days, through: :levels #for being able to access Habit.find(*).days
accepts_nested_attributes_for :levels, :days
before_save :set_level
acts_as_taggable
serialize :committed, Array
def evaluate(user)
levels.each { |level| level.evaluate }
user.missed_levels << levels.where(passed: false).ids
user.missed_days << days.where(missed: true).ids
user.save
end
def self.committed_for_today
today_name = Date::DAYNAMES[Date.today.wday].downcase
ids = all.select { |h| h.committed.include? today_name }.map(&:id)
where(id: ids)
end
def levels
committed_wdays = committed.map { |day| Date::DAYNAMES.index(day.titleize) }
n_days = ((date_started.to_date)..Date.today).count { |date| committed_wdays.include? date.wday }
case n_days
when 0..9
1
when 10..24
2
when 25..44
3
when 45..69
4
when 70..99
5
else
"Mastery"
end
end
private
def set_level
self.level = levels
end
end
The logic behind it all is that a User creates a habit he wants to challenge. To achieve "mastery" in the habit he must complete 5 levels. Each level has a certain amount of :committed days that must be completed before advancing, as broken down above with n_days.
The User commits in the _form to what days (Sun thru Sat) he wants to do the habit. For example he could just choose sun, wed, sat. Then the app should only calculate n_days according to non-:missed :committed days (once 10 of those days passes it moves onto the 2nd level).
class Level < ActiveRecord::Base
belongs_to :habit
belongs_to :user
has_many :days
accepts_nested_attributes_for :days
def evaluate
if days.where(missed: true ).count == days_needed
update_attributes(passed: false)
else
update_attributes(passed: true)
end
end
end
class Day < ActiveRecord::Base
belongs_to :level
belongs_to :habit
end
class HabitsController < ApplicationController
before_action :set_habit, only: [:show, :edit, :update, :destroy]
before_action :logged_in_user, only: [:create, :destroy]
def index
if params[:tag]
#habits = Habit.tagged_with(params[:tag])
else
#habits = Habit.all.order("date_started DESC")
#habits = current_user.habits
end
end
def show
end
def new
#goal = current_user.goals.build
#habit = current_user.habits.build
#level = current_user.levels.build
3.times { #level.days.build }
end
def edit
end
def create
#habit = current_user.habits.build(habit_params)
#levels = #habit.levels
if #habit.evaluate(#user)
redirect_to #habit, notice: 'Habit was successfully created.'
else
#feed_items = []
render 'pages/home'
end
end
def update
if #habit.update(habit_params)
redirect_to #habit, notice: 'Habit was successfully updated.'
else
render action: 'edit'
end
end
def destroy
#habit.destroy
redirect_to habits_url
end
private
def set_habit
#habit = Habit.find(params[:id])
end
def correct_user
#habit = current_user.habits.find_by(id: params[:id])
redirect_to habits_path, notice: "Not authorized to edit this habit" if #habit.nil?
end
def habit_params
params.require(:habit).permit(
:user_id,
:level,
:left,
:date_started,
:trigger,
:target,
:positive,
:negative,
:tag_list,
:committed => [],
:levels_attributes => [
:passed,
:days_attributes => [
:missed,:level_id]])
end
end
<%= simple_form_for(#habit) do |f| %>
<%= f.error_notification %>
<div class="america">
<form>
<div class="committed">
<%= f.label "Committed to:" %>
<%= f.collection_check_boxes :committed, Date::DAYNAMES, :downcase, :to_s %>
</div>
<p>
<div class="date-group">
<label> Started: </label>
<%= f.date_select :date_started, :order => [:month, :day, :year], class: 'date-select' %>
</div>
</p>
<p>
<%= f.text_field :trigger, class: 'form-control', placeholder: 'Enter Trigger' %></p>
<p>
<%= f.text_field :tag_list, habit: #habit.tag_list.to_s.titleize, class: 'form-control', placeholder: 'Enter Action' %>
</p>
<p>
<%= f.text_field :target, class: 'form-control', placeholder: 'Enter Target' %>
</p>
<p>
<%= f.text_field :positive, class: 'form-control', placeholder: 'Enter Reward' %>
</p>
<% 5.times.each_with_index do |number, index| %>
<h1>Level <%= index + 1 %></h1>
<%= f.fields_for :levels do |level| %>
<%= level.fields_for :days do |day| %>
<%= day.label :missed %>
<%= day.check_box :missed %> <br/>
<% end %>
<% end %>
<% end %>
<div class="america2">
<%= button_tag(type: 'submit', class: "btn") do %>
<span class="glyphicon glyphicon-plus"></span>
<% end %>
<%= link_to habits_path, class: 'btn' do %>
<span class="glyphicon glyphicon-chevron-left"></span>
<% end %>
<%= link_to #habit, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn' do %>
<span class="glyphicon glyphicon-trash"></span>
<% end %>
</div>
</form>
</div>
<% end %>
<!-- Default bootstrap panel contents -->
<div id="valuations" class="panel panel-default">
<div class="panel-heading"><h4><b>HABITS</b></h4></div>
<!-- Table -->
<table>
<thead>
<tr>
<th>Level</th>
<th>Left</th>
<th>Strike</th>
<th>Trigger</th>
<th>Action</th>
<th>Target</th>
<th>Reward</th>
<th>Days</th>
</tr>
</thead>
<tbody>
<% #habits.each do |challenged| %>
<tr>
<td><%= challenged.level %></td>
<td><%= challenged.left %></td>
<td>
<%= link_to edit_habit_path(challenged) do %>
<%= [params[:missed]].flatten.length %>
<% end %></td>
<td><%= challenged.trigger %></td>
<td class="category">
<b><%= raw challenged.tag_list.map { |t| link_to t.titleize, taghabits_path(t) }.join(', ') %></b>
</td>
<td><%= challenged.target %></td>
<td><%= challenged.positive %></td>
<td class= "committed">
<%= challenged.committed.map { |d| d.titleize[0,3] }.join ', ' %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
Thank you so much for your help!
Some of this code came from this answer here: How to integrate :missed days with :committed days in habits.rb? which messed up what worked with this answer: How to Make :level Change Based on :committed Days?
It appears that date_started is an attribute of your Habit model, probably a database column, and that there are NULLs in date_started. Open up your Rails console and see if this is the case with:
Habit.where(date_started: nil).count
If you expect that date_started should never be null, add a validation to ensure that is the case. As soon as you test the code which is saving nulls into that column, the validation errors will point you to the bug.
On the other hand, if you want to allow nulls in date_started, then rewrite your levels method to allow for that.

Displaying items captured via a nested form

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 %>

Resources