Rails 4 fields_for not displaying or updating - ruby-on-rails

I have a nested relationship where dashboard has many rewards, and I am trying to add a fields_for to the page in order to edit the rewards. Unfortunately, it doesn't seem to be working and I don't know why.
Here's what I have.
Dashboard model:
class Dashboard < ActiveRecord::Base
belongs_to :manager
has_many :rewards
accepts_nested_attributes_for :rewards, allow_destroy: true
end
Rewards model:
class Reward < ActiveRecord::Base
belongs_to :dashboard
end
Dashboard controller:
class DashboardsController < ApplicationController
before_action :authenticate_manager!
# Requires user to be signed in
def index
#dashboards = Dashboard.all
end
def new
#dashboard = Dashboard.new
end
def edit
#dashboard = Dashboard.find(params[:id])
end
def create
#dashboard = Dashboard.new(dashboard_params)
#dashboard.save
if #dashboard.save
redirect_to dashboard_path(#dashboard)
else
render :action => new
end
end
def update
#dashboard = Dashboard.find(params[:id])
if #dashboard.update(dashboard_params)
redirect_to :action => :show
else
render 'edit'
end
end
def show
#dashboard = Dashboard.find(params[:id])
end
def destroy
#dashboard = Dashboard.find_by_id(params[:id])
if #dashboard.destroy
redirect_to dashboards_path
end
end
private
def dashboard_params
args = params.require(:dashboard).permit(:title, :description, :rewards, {rewards_attributes: [ :id, :title, :referralAmount, :dashboardid, :selected, :_destroy] } )
args
end
end
Form in dashboards view:
<%= form_for :dashboard, url: dashboard_path(#dashboard), method: :patch do |f| %>
<% if #dashboard.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#dashboard.errors.count, "error") %> prohibited
this dashboard from being saved:
</h2>
<ul>
<% #dashboard.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_field :description %>
</p>
<%= f.fields_for :rewards do |reward| %>
<%= reward.label :title %><br>
<%= reward.text_field :title %>
<%= reward.check_box :_destroy %>
<%= reward.label :_destroy, "Remove reward" %>
<% end %>
<p>
<%= f.submit %>
</p>
<% end %>
I went ahead and manually added rewards to the database through the rails console and it worked beautifully, but they are not showing up on the page. They will show up if I iterate through them like so
<% if #dashboard.rewards.any? %>
<ul>
<% #dashboard.rewards.each do |reward| %>
<li><%= reward.title %></li>
<li><%= reward.referralAmount %></li>
<% end %>
</ul>
<% else %>
<p>no rewards</p>
<% end %>
However the fields_for does not display the rewards or their content and resultingly allow one to edit them.
Let me know if you need further information/code.

Try to modify your:
View:
<% if #dashboard.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#dashboard.errors.count, "error") %> prohibited
this dashboard from being saved:
</h2>
<ul>
<% #dashboard.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form_for #dashboard, url: dashboard_path(#dashboard) do |f| %>
........
<% end %>
Controller (has_many relationship):
def new
#dashboard = Dashboard.new
#dashboard.rewards.build
end
private
def dashboard_params
params.require(:dashboard).permit(:title, :description,
rewards_attributes: [
:id,
:title,
:referralAmount,
:dashboardid,
:selected,
:_destroy
])
end
You don't have to set the method: patch if form.
Once you got in edit page, Rails will use the update action in controller when form submission.
To check it, run rake routes,
you will see somsthing like this:
PATCH /dashboards/:id(.:format) dashboards#update
PUT /dashboards/:id(.:format) dashboards#update

In controller you need to give build
def new
#dashboard = Dashboard.new
#dashboard.rewards.build
end
"build" is just create a new object in memory so that the view can take this object and display something, especially for a form.
Hope it helps for you

You should build object before nested form. You can add whatever you want that object.
Try it in controller;
def new
#dashboard = Dashboard.new
3.times do
#dashboard.build_reward
end
end

Try setting an "#rewards" instance variable in your dashboards edit method (where #rewards = #dashboard.rewards). Then replace :rewards with #rewards.
Edit:
I believe my initial answer is inapproriate for your exact question (while it would be helpful on say the page to show a specific dashboard and its rewards). The answers above are on the right track re:
refining your params method per #aldrien.h;
Adding #santosh dadi's suggestion of
#dashboard.rewards.build
(assuming you only want one rewards fields on a form for "new")
Finally though, to avoid making fake information for a new rewards form, adding to the top of your Dashboards model:
accepts_nested_attributes_for :rewards, reject_if: lambda {|attributes| attributes['title'].blank?}
http://guides.rubyonrails.org/form_helpers.html#nested-forms

Related

Simple form for not working on belongs to

Hello I have a simple rails app that has two models a goal and a task
The goal has many tasks, and a task belongs to a goal.
For some reason, probably a rookie error, I cannot get the form to the task form to render with simple form.
Models
Goal
class Goal < ApplicationRecord
has_many :tasks
end
Task
class Task < ApplicationRecord
belongs_to :goal
end
Controllers
Goals
class GoalsController < ApplicationController
before_action :set_goal, only: [:show]
def show
end
private
def set_goal
#goal = Goal.find(params[:id])
end
end
View views/goals/show
<div class="row">
<%= #goal.title %>
<div class="row">
<ul>
<% #goal.tasks.each do |task| %>
<li><%= task.name %></li>
<% end %>
</ul>
<%= render partial: 'tasks/form', locals: {comment: #goal.tasks.new} %>
</div>
Form views/tasks/_form
<%= simple_form_for([#goal, #task]) do |f| %>
<div class="form-inputs">
<%= f.input :name %>
<%= f.input :description %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
I get the error NoMethodError in Goals#show
so obviously I need to add the #task to my goals show.... but how
so I added to my goals show method
#task = Task.find_or_create_by(:task_id)
then i get the error
Unsupported argument type: task_id (Symbol)
so I added the following to my goals_controller
def show
#task = Goal.task.find_or_create_by(:task_id)
end
but then I get
NoMethodError in GoalsController#show
undefined method `task' for #<Class:0x00007ff8c79b0920> Did you mean? take
Routes
Rails.application.routes.draw do
resources :tasks
resources :goals
end
As per Jagdeep's comment above adding Try adding #task = #goal.tasks.build in goals_controller#show fixed this issue.
hope this helps

Rails form - multiple nested routes undefined method '_path'

This app has the following models:
Farm (has_many :crops)
Crop (belongs_to :farm, has_many :issues)
Issue (belongs_to :crop)
Here are the routes:
resources :farms do
resources :crops do
resources :issues
end
end
I want a user to be able to create a new "issue" from the Farm#show page that lists all the farm's crops. Here is the form that is causing the error on the Farm#show page:
undefined method `crop_issues_path' for #<#:0x007fa814a3cc30>
#from the show action on the controller:
##farm = Farm.find(params[:id])
##crops = #farm.crops
<% #crops.each do |crop| %>
<%= crop.id %>
<%= form_for([crop, crop.issues.build]) do |f| %>
<%= f.select(:issue_type, options_for_select([['mold'], ['pests'], ['dehydration'], ['other']])) %>
<%= f.text_area :notes %><br>
<%= f.submit "New Issue", :class => "button" %>
<% end %>
<% end %>
My create action on issues controller:
def create
#crop = Crop.find(params[:crop_id])
#issues = #crop.issues.create(params[:issue].permit(:issue_type, :notes, :crop_id))
redirect_to :back
end
I have used nearly identical code when the crops and issues were not nested under farms, and it works. I believe the issue is because of the nesting, but cannot figure out a solution.
I think your problem is with the object you're binging the form to. It should be #farm, as you're in the #farms show action.
I modified it to this:
<% #crops.each do |crop| %>
<%= crop.id %>
<%= form_for([#farm, crop, crop.issues.build]) do |f| %>
<%= f.text_area :notes %><br>
<%= f.submit "New Issue", :class => "button" %>
<% end %>
<% end %>
with my controller like this:
class FarmsController < ApplicationController
def index
end
def show
#farm = Farm.find_by_id(params[:id])
#crops = #farm.try(:crops)
end
end

Rails simple_form nested attributes

I have 2 models:
class Client < ActiveRecord::Base
has_many :contact_people
accepts_nested_attributes_for :contact_people
end
class ContactPerson < ActiveRecord::Base
belongs_to :client
end
I cand add a new Client or a new ContactPerson separately with no problem.
I want to create a form where I will be able to add them both with a nested form. What would be the recommended way to do this, create a new controller to do this and create new and create action for that, or use the ClientsController and create a new method there?
If a new controller is recommended, how can I access the params? Also, will validations will work here?
Thanks!
You should be using the new and create methods of your ClientsController to do something like this
Class ClientsController < ApplicationController
def new
#client = Client.new
#client.contact_people.build #this is very important
end
def create
#client = Client.new(client_params)
if #client.save
redirect_to #client
else
render 'new'
end
end
private
def client_params
params.require(:client).permit(:client_attr1, :client_attr2,.., contact_people_attributes: [:id, :contact_people_attr1,:contact_people_attr2,..])
end
end
And the view code would be something like this
<%= simple_form_for #client, :html => { :multipart => true } do |f| %>
<% if #client.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#client.errors.count, "error") %> prohibited this client from being saved:</h2>
<ul>
<% #client.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
#your client attributes code here
<%= f.simple_fields_for :contact_people do |cp| %>
#your contact_people attributes code here
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And the validations you are talking about can be set normally in models.

Nested form fields_for text_area is not displaying

I have three-tier model:
User has_many Asks has_many Outcomes
On the home page, I would like the user to be able to add an Outcome to their Ask when they mark it complete. I'm trying to use a nested form to display the Outcome description in the Ask form which also updates the done flag and done date.
Like other users/questions here on SO, I cannot get a nested form to display on the screen. I've followed instructions from the other questions, but still the nested field is not displaying. Am wondering if someone can spot the issue in the code below?
Ask Model
class Ask < ActiveRecord::Base
attr_accessible :category, :description, :done, :followed_up,
:helper, :public, :date_done, :date_followed_up, :user_id, :outcomes_attributes
belongs_to :user, counter_cache: true
has_many :outcomes
accepts_nested_attributes_for :outcomes
end
Ask Controller
class AsksController < ApplicationController
def new
#ask = current_user.asks.build(params[:ask])
#ask.outcomes.build
end
def create
#ask = current_user.asks.build(params[:ask])
if #ask.save!
respond_to do |format|
format.html { redirect_to edit_ask_path(#ask) }
format.js
end
else
flash[:error] = "Something is wrong. The Ask was not saved..."
end
end
def edit
#ask = current_user.asks.find(params[:id])
end
def update
#ask = current_user.asks.find(params[:id])
#ask.outcomes.build
#ask.update_attributes(params[:ask])
respond_to do |format|
format.html { redirect_to edit_ask_path(#ask) }
format.js
end
end
end
Home Page Controller (this form is on the home page)
class StaticPagesController < ApplicationController
def home
if signed_in?
#ask = current_user.asks.build(params[:ask])
#ask.outcomes.build
end
end
Form Partial rendered on the home page
<% if current_user.asks.any? %>
<ul id="ask-list-items">
<% current_user.asks.where(done: false).each do |a| %>
<%= form_for(a) do |f| %>
<li><%= a.description %></li>
<%= f.hidden_field :date_done, value: Date.today %>
<%= f.hidden_field :done, :value=>true %>
<%= f.submit "Mark as done", class: "btn btn-small hidden done_btn", id: "a-#{a.id}-done" %>
<%= f.fields_for :outcomes do |builder| %> # << These fields are not showing up
<%= builder.text_area :description, placeholder: "Describe the outcome...", id: "ask-message" %>
<% end %>
<%= f.submit "Save outcome", class: "btn btn-primary" %>
<% end %>
<% end %>
</ul>
<% end %>
When using symbol in form_for and fields_for Rails tries to use an instance variable with he same name, e.g. #outcomes for :outcomes. So try (for existing outcomes):
<% #outcomes = a.outcomes %>
before the line with f.fields_for :outcomes....
And for new outcomes:
<% #outcomes = a.outcomes.build %>
(the last with contribution to the owner of the question)

Updating multiple records massing assignment error

I'm working on a shows when a store was last visited. I want to be able to update multiple stores at once if they were all visited on the same day.
I think I have most of the code but I can't figure out how to get rid of the mass assignment error
Can't mass-assign protected attributes: date_visited(1i), date_visited(2i), date_visited(3i)
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"/yr8kLOyrTRGPfG1f/I5ilY/QB6GUx9IhQj6WiBaibM=",
"store_ids"=>["4",
"5"],
"visit"=>{"date_visited(1i)"=>"2012",
"date_visited(2i)"=>"11",
"date_visited(3i)"=>"14"},
"commit"=>"Save Visit"}
Model
class Visit < ActiveRecord::Base
attr_accessible :date_visited, :spent, :store_id
belongs_to :
end
Controller
def update_multiple
#visits = Store.find(params[:store_ids])
#visits.each do |visit|
visit.update_attributes(params[:visit])
end
flash[:notice] = "Updated products!"
redirect_to stores_path
end
View
<%= form_for :visit, :url => update_multiple_visits_path, :html => { :method => :put } do |f| %>
<ul>
<% #visits.each do |visit| %>
<%= hidden_field_tag "store_ids[]", visit.id %>
<% end %>
</ul>
<div class="field">
<%= f.label :date_visited %><br />
<%= f.date_select :date_visited %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<ol id="route">
<% #visits.each do |store| %>
<%= content_tag_for :li, store do %>
<%= "#{store.store} - #{store.address}" %>
<% end %>
<% end %>
</ol>
Most likely, you are missing the attr_accessible :your_model_attributes is this case, :visits_attributes on your activerecord model definition.
Also, your params should look like
{ visits =>
{ id_1 =>
{ :store_id
:attributes_for_visit_1 }
}
{ id_2 =>
{ :store_id
:attributes_for_visit_2 }
}
} # and so on....
# visits_controller.rb
def update_nultiple_visits
#visits = Visits.find(params[:visits].keys).each{|visit|visit.update_attributes!}
end
Add this to your Store model
attr_accessible :visits_attributes
accepts_nested_attributes_for :visits
And I'd suggest changing your controller to this:
def update_multiple
#stores = Store.find(params[:store_ids])
#stores.each do |store|
store.update_attributes(params[:visit])
end
flash[:notice] = "Updated products!"
redirect_to stores_path
end
Helper date_select generate three a select tags (for year, month and day).
You can concatenate its before updating attributes.
For example:
Date.civil(params[:visit][:date_visited(1i)].to_i, params[:visit][:date_visited(2i)].to_i, params[:visit][:date_visited(3i)].to_i)

Resources