I am getting the following error:
NoMethodError in Eurekamoments#new
undefined method `field' for #<Eurekamoment:0x52c6f80>
on this line:
<%= f.text_field :field %>
from this view
This is new view
<%= form_for(#eurekamoment) do |f| %>
<% if #eurekamoment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#eurekamoment.errors.count, "error") %> prohibited this link from being saved:</h2>
<ul>
<% #eurekamoment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :eurekamoment %><br />
<%= f.text_field :eurekamoment %>
</div>
<div class="field">
<%= f.label :field %><br />
<%= f.text_field :field %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
This is my controller:
class EurekamomentsController < ApplicationController
def index
#eurekamoment = Eurekamoment.paginate(:page => params[:page], :per_page => 3 )
params[:per_page] ||= 25
params[:page] ||= 1
end
def show
#eurekamoment = Eurekamoment.find(params[:id])
#comment = Comment.new
#vote = Vote.new
end
def new
#eurekamoment = Eurekamoment.new
end
def create
#eurekamoment = Eurekamoment.new(link_params)
if #eurekamoment.save
redirect_to #eurekamoment
else
render action: 'new'
end
end
def destroy
#eurekamoment = Eurekamoment.find(params[:id])
if #eurekamoment.destroy
redirect_to action: 'index'
else
render action: 'show'
end
end
def edit
#eurekamoment = Eurekamoment.find(params[:id])
end
def update
#eurekamoment = Eurekamoment.find params[:id]
if #eurekamoment.update(eurekamoment_params)
redirect_to #eurekamoment
else
render action: 'edit'
end
end
private
def eurekamoment_params
params.require(:eurekamoment).permit(:id, :eurekamoment, :field, :description, :plan)
end
end
This is my model:
class Eurekamoment < ActiveRecord::Base
has_many :comments
belongs_to :user
has_many :votes
end
And my db:
class CreateEurekamoments < ActiveRecord::Migration
def change
create_table :eurekamoments do |t|
t.integer :user_id
t.string :field
t.string :eurekamoment
t.string :description
t.string :plan
t.timestamps
end
end
end
I've wasted last hour trying to figure this out at a hackathon and can't fix this issue, do you guys see anything wrong? Thanks.
Your code looks good - the error will almost certainly be a DB issue, as discussed in your comments
I think this will be the real cause of your problem: I've wasted last hour trying to figure this out at a hackathon and can't fix this issue -- it's best to take a step back, deep breath and work through it logically
I'd start with a total db refresh. Drop your tables & then run rake db:migrate again, or you could use rake db:migrate VERSION=0, or rake db:migrate:reset - How can I migrate my database with rails to the first revision without dropping the database first?
Related
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
The example code below is a contrived example of an attempt at a form object where it is probably overkill to utilize a form object. Nonetheless: it shows the issue I am having:
I have two models: a User and an Email:
# app/models/user.rb
class User < ApplicationRecord
has_many :emails
end
# app/models/user.rb
class Email < ApplicationRecord
belongs_to :user
end
I want to create a form object which creates a user record, and then creates three associated email records.
Here are my form object classes:
# app/forms/user_form.rb
class UserForm
include ActiveModel::Model
attr_accessor :name, :email_forms
validates :name, presence: true
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
puts "The Form is VALID!"
puts "I would proceed to create all the necessary objects by hand"
user = User.create(name: name)
email_forms.each do |email|
Email.create(user: user, email_text: email.email_text)
end
end
end
# app/forms/email_form.rb
class EmailForm
include ActiveModel::Model
attr_accessor :email_text, :user_id
validates :email_text, presence: true
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
puts "The Form is VALID!"
# DON'T THINK I WOULD PERSIST DATA HERE
# INSTEAD DO IT IN THE user_form
end
end
Notice: the validations on the form objects. A user_form is considered to be invalid if it's name attribute is blank, or if the email_text attribute is left blank for any of the email_form objects inside it's email_forms array.
For brevity: I will just be going through the new and create action of utilizing the user_form:
# app/controllers/user_controller.rb
class UsersController < ApplicationController
def new
#user_form = UserForm.new
#user_form.email_forms = [EmailForm.new, EmailForm.new, EmailForm.new]
end
def create
#user_form = UserForm.new(user_form_params)
if #user_form.save
redirect_to users_path, notice: 'User was successfully created.'
else
render :new
end
end
private
def user_form_params
params.require(:user_form).permit(:name, {email_forms: [:_destroy, :id, :email_text, :user_id]})
end
end
Lastly: the form itself:
# app/views/users/new.html.erb
<h1>New User</h1>
<%= render 'form', user_form: #user_form %>
<%= link_to 'Back', users_path %>
# app/views/users/_form.html.erb
<%= form_for(user_form, url: users_path) do |f| %>
<% if user_form.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user_form.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user_form.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
# MESSY, but couldn't think of a better way to do this...
<% unique_index = 0 %>
<% user_form.email_forms.each do |email_form| %>
<div class="field">
<%= label_tag "user_form[email_forms][#{unique_index}][email_text]", "Email Text" %>
<%= text_field_tag "user_form[email_forms][#{unique_index}][email_text]" %>
</div>
<% unique_index += 1 %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The form does render:
And here is the form's html:
I go to submit the form. Here is the params hash:
Parameters: {"utf8"=>"✓", "authenticity_token"=>”abc123==", "user_form"=>{"name"=>"neil", "email_forms"=>{"0"=>{"email_text"=>"test_email_1"}, "1"=>{"email_text"=>"test_email_2"}, "2"=>{"email_text"=>""}}}, "commit"=>"Create User form"}
What should happen is the form should be re-rendered and nothing persisted because the form_object is invalid: All three associated emails must NOT be blank. However: the form_object thinks it is valid, and it blows up in the persist! method on the UserForm. It highlights the Email.create(user: user, email_text: email.email_text) line and says:
undefined method `email_text' for ["0", {"email_text"=>"test_email_1"}]:Array
Clearly there are a couple things going on: The nested validations appear to not be working, and I am having trouble rebuilding each of the emails from the params hash.
Resources I have already examined:
This Article seemed promising but I was having trouble getting it to work.
I have attempted an implementation with the virtus gem and the reform-rails gem. I have pending questions posted for both of those implementations as well: virtus attempt here and then reform-rails attempt here.
I have attempted plugging in accepts_nested_attributes, but was having trouble figuring out how to utilize that with a form object, as well as a nested form object (like in this code example). Part of the issue was that has_many and accepts_nested_attributes_for do not appear to be included in ActiveModel::Model.
Any guidance on getting this form object to do what is expected would be very much appreciated! Thanks!
Complete Answer
Models:
#app/models/user.rb
class User < ApplicationRecord
has_many :emails
end
#app/models/email.rb
class Email < ApplicationRecord
belongs_to :user
end
Controller:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def index
#users = User.all
end
def new
#user_form = UserForm.new
#user_form.emails = [EmailForm.new, EmailForm.new, EmailForm.new]
end
def create
#user_form = UserForm.new(user_form_params)
if #user_form.save
redirect_to users_path, notice: 'User was successfully created.'
else
render :new
end
end
private
def user_form_params
params.require(:user_form).permit(:name, {emails_attributes: [:email_text]})
end
end
Form Objects:
#app/forms/user_form.rb
class UserForm
include ActiveModel::Model
attr_accessor :name, :emails
validates :name, presence: true
validate :all_emails_valid
def emails_attributes=(attributes)
#emails ||= []
attributes.each do |_int, email_params|
email = EmailForm.new(email_params)
#emails.push(email)
end
end
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
user = User.new(name: name)
new_emails = emails.map do |email_form|
Email.new(email_text: email_form.email_text)
end
user.emails = new_emails
user.save!
end
def all_emails_valid
emails.each do |email_form|
errors.add(:base, "Email Must Be Present") unless email_form.valid?
end
throw(:abort) if errors.any?
end
end
app/forms/email_form.rb
class EmailForm
include ActiveModel::Model
attr_accessor :email_text, :user_id
validates :email_text, presence: true
end
Views:
app/views/users/new.html.erb
<h1>New User</h1>
<%= render 'form', user_form: #user_form %>
<%= link_to 'Back', users_path %>
#app/views/users/_form.html.erb
<%= form_for(user_form, url: users_path) do |f| %>
<% if user_form.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user_form.errors.count, "error") %> prohibited this User from being saved:</h2>
<ul>
<% user_form.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<%= f.fields_for :emails do |email_form| %>
<div class="field">
<%= email_form.label :email_text %>
<%= email_form.text_field :email_text %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
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
I'm trying to add a simple Star Rating system for my app having taken this tutorial for an example. I have User, Hotel and Rating models. Dependencies are:
(rating.rb)
belongs_to :user
belongs_to :hotel
(hotel.rb) & (user.rb)
has_many :ratings
And with the following code in hotel view I get this error:
NameError in Hotels#show
undefined local variable or method `user' for Class...
(in the line with <%= form_for...)
Hotel view (show.html.erb):
<% form_id = "hotel_#{#hotel.id}_rating" %>
<% if signed_in? %> <!-- To avoid throwing an exception if no user is signed in -->
<% user_id = current_user.id %>
<% else %>
<% user_id = -1 %>
<% end %>
<%= form_for #hotel.ratings.find_or_create_by_user_id user.id,
:html => {:id => form_id,
:class => "star_rating_form"} do |f| %>
<%= f.hidden_field :hotel_id, :value => #hotel.id %>
<% if signed_in? %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<% end %>
<%= f.hidden_field :stars, :id => form_id + "_stars" %>
<% end %>
<% (1..5).each do |i| %>
<li class="rating_star" id="<%= form_id %>_<%= i %>" data-stars="<%= i %>" data-form-id="<%= form_id %>"></li>
<% end %>
Ratings controller is:
def create
end
def update
end
def rating_params
params.require(:rating).permit(:stars)
end
Migration file is:
create_table :ratings do |t|
t.integer :stars, :default => 0
t.references :store
t.references :user
end
From the comments, the error seems to be here:
#hotel.ratings.find_or_create_by_user_id user.id
--
user_id
The problem is your show view doesn't have access to a local variable called user
This variable should either be defined in the controller (which would mean it has to be an #instance variable, or should be a helper (such as current_user.id)
The fix should therefore be as follows:
<% user_id = user_signed_in? ? current_user.id : "-1" %>
<%= form_for #hotel.ratings.find_or_create_by_user_id user_id ...
This should get it working for you with the code you have provided. As you've not provided the new action from the controller, I don't know whether the supporting structure for the code will be correct or not.
After some search on find_or_create_by, I changed line with 'form_for' into
<%= form_for #hotel.ratings.find_or_create_by(user_id: user_id)
That solved the issue!
Thanks to all for your support!
I'm not sure how to display the error messages for my form when using it in this form_tag scenario. My code below allows me to create 5 products at once on a form but unfortunately only renders the notice that "an error occurred...".
Here is my code:
Product.rb
class Product < ActiveRecord::Base
attr_accessible :price, :name, :purchase_date, :product_store, :in_category
belongs_to :user
belongs_to :store
attr_reader :product_store
validates_inclusion_of :in_category, :in => [true, false]
validates_presence_of :name, :price, :store_id, :user_id
validates_numericality_of :price
def product_store=(id)
self.store_id = id
end
end
Products_controller.rb
class ProductsController < ApplicationController
def new
#products = Array.new(5) { Product.new }
end
def create_multiple
#products = current_user.products.create(params[:products].map { |_k, p| p.merge params[:product] })
if #products.each(&:save)
redirect_to :back, :notice => "Success!"
else
redirect_to :back, :notice => "An error occured, please try again."
end
end
end
Form.html.erb
<%= form_tag create_multiple_products_path, :method => :post do %>
<%= error_messages_for #product %>
# the :purchase_date and :in_category are merged into all 5 Products.
<%= date_select("product", "purchase_date") %>
<%= label_tag :in_category, 'Add to Category?' %>
<%= radio_button("product", :in_category, 1) %>
<%= radio_button("product", :in_category, 0) %>
<% #products.each_with_index do |product, index| %>
<%= fields_for "products[#{index}]", product do |p| %>
<%= render "fields", :f => p %>
<% end %>
<% end %>
<%= submit_tag "Done" %>
<% end %>
Theirs 2 issues. 1. Getting the validations for the fields outside of the fields_for to show .2. And then the ones inside of the fields_for. How could I do this?
Thank you.
I've been trying to do much the same thing, with this:
<% #products.each_with_index do |product, index| %>
<% product.errors.full_messages.each do |value| %>
<li><%= value %></li>
<% end %>
However, this only shows errors for the first product with errors. You submit it, and if there is a subsequent product with errors, you are sent back to that page, and that next product with errors shows its errors, etc.
EDIT: Got it. It has to do with how I was validating. Instead of this:
if #products.all?(&:valid?)
do this:
#products.each(&:valid?) # run the validations
if #products.all? { |t| t.errors.empty? }