I am trying to incorporate Simple_forms into my app using a virtual attributes. I am following http://railscasts.com/episodes/102-auto-complete-association-revised to get autocomplete to work as well. Simple_form works when i do not use a virtual attribute but when I do use the virtual attribute I get the error "Association :head_coach_name not found".
My Team Model is:
class Team < ActiveRecord::Base
attr_accessor :head_coach_name
belongs_to :user
validates :team_name, presence: true
belongs_to :head_coach, class_name: "User", :foreign_key => "head_coach_id"
def head_coach_name
user.try(:name)
end
def head_coach_name=(name)
self.user = User.find_by_name(name) if name.present?
end
end
My User model is:
class User < ActiveRecord::Base
has_many :teams, :class_name => "::Team", dependent: :destroy
end
My View:
<%= simple_form_for #team, html: {class: 'form-horizontal' }, url: teams_path, method: 'post' do |f| %>
<%= f.error_notification %>
<%= f.hidden_field :user_id %>
<div class="col-md-6">
<div class="row">
<div class="col-md-12">
<%= f.input :team_name, class: "form-control" %>
<%= f.input :year, collection: Date.today.year-90..Date.today.year %>
<%= f.input :season, as: :select, collection:
['Fall',
'Winter',
'Spring',
'Summer'] %>
<%= f.input :season_type, as: :select, collection:
['Outdoor',
'Indoor',
'Tournament'] %>
<%= f.input :awards %>
</div>
</div>
</div>
<div class="col-md-6">
<div class="row">
<div class="col-md-12">
<%= f.input :club %>
<%= f.input :division %>
<%= f.association :head_coach_name %>
<%= f.input :assistant_coach_id %>
<%= f.input :assistant_coach_two_id %>
</div>
</div>
</div>
<%= f.button :submit, label: "Add team", class: "btn btn-large btn-primary col-md-3 pull-right" %>
<% end %>
Everything works as long as i don't have the virtual association in there. I could put :head_coach and it would work with a drop down list but I want to use the autocomplete feature like in the railscast video. Also in rails console i can run these commands to show the virtual attribute works:
2.1.2 :003 > team.head_coach_name
User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 100 LIMIT 1
=> "Miss Daisha Shanahan"
Any ideas on how I can get the virtual attribute to work correctly with simple_forms?
You are referencing an association that doesn't exist with f.association :head_coach_name. Try this instead:
f.association :head_coach, label_method: :head_coach_name
You have a few other oddities in your code, including your head_coach_name definition, which should rely on head_coach instead of user. Same concern for the head_coach_name= method.
Example. You have:
def head_coach_name
user.try(:name)
end
Seems like you should have:
def head_coach_name
head_coach.try(:name)
end
Related
When I submit the form it is passing {"utf8"=>"✓", "authenticity_token"=>"blah", "client"=>{"first_name"=>"jack", "last_name"=>"kool","complaints"=>{"symptom"=>"burnt"} in the params and I am getting Unpermitted Parameters: complaints Since complaints is nested in Client, it should be passing complaints_attributes, like I have it set up in the strong params, and I can't figure out why it isn't.
class ClientsController < ApplicationController
def new
#client = Client.new
end
def edit
#client = Client.find(params[:id])
end
def create
#client = Client.new(client_params)
if #client.save
redirect_to #client
else
render 'new'
end
end
def update
#client = Client.find(params[:id])
if #client.update(client_params)
redirect_to #client
else
render 'edit'
end
end
private
def client_params
params.require(:client).permit(:first_name, :last_name, complaints_attributes: [ :symptom, :id ])
end
end
Client model:
class Client < ActiveRecord::Base
has_many :complaints
has_one :personal_disease_history
has_one :family_disease_history
has_many :surgeries
has_many :hospitalizations
has_many :medications
has_many :allergies
validates :first_name, :last_name, presence: true
accepts_nested_attributes_for :complaints
end
Complaint model:
class Complaint < ActiveRecord::Base
belongs_to :client
end
Form:
<%= form_for :client, url: clients_path, html: { class: "form-inline" } do |f| %>
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">Health History Form</h3>
</div>
<div class="panel-body">
<div class="form-blocks"><legend>Name</legend>
<div class="form-group">
<%= f.label :first_name, class: "sr-only" %>
<%= f.text_field :first_name, class: "form-control", placeholder: "First Name" %>
</div>
<div class="form-group">
<%= f.label :last_name, class: "sr-only" %>
<%= f.text_field :last_name, class: "form-control", placeholder: "Last Name" %>
</div>
</div>
<div class="form-blocks"><legend>Complaints</legend>
<div class="form-group">
<%= f.fields_for :complaints do |builder| %>
<%= builder.label :symptom, class: "sr-only" %>
<%= builder.text_field :symptom, class: "form-control", placeholder: "Symptom" %>
<% end %>
</div>
</div>
<div>
<%= f.submit "Submit Form", class: "btn btn-primary btn-lg"%>
</div>
<% end %>
inside client controller create action, try saying this inside the if #client.save:
Complaint.create(symptom: params[
"client"]["complaints"]["symptom"], client_id: #client.id)
the unpermitted parameter is related to the nested attribute problem but this should get the actual record saving. If that doesn't do it, please post the stack trace surrounding the commit.
have couple other ideas: but (1) you have complaint_attributes in the permitted params but you could change that to complaint bc that's what's coming through in the params.
My user model contains values for things like:
name, email, phone, approved, etc..
The scope of the project I am working on includes custom user settings and for this reason I am using hstore (I dont want to create a user_settings table).
On the user edit view I want to create a checkbox called 'colors' and whether or not the checkbox is checked determines if that setting is set for the user or not.
I have already setup hstore as follows:
class AddHstore < ActiveRecord::Migration
def up
enable_extension :hstore
end
def down
disable_extension :hstore
end
end
And updated the User model as follows:
class AddSettingsToUser < ActiveRecord::Migration
def up
add_column :users, :settings, :hstore
end
def down
remove_column :users, :settings
end
end
This is essentially my user edit view:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, :class => "form-horizontal user" }) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name, autofocus: true %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email %>
</div>
<div class="field">
<%= f.label :phone_number %><br />
<%= f.text_field :phone_number %>
</div>
<div class="actions">
<%= f.submit 'Update', :class => 'btn btn-primary' %>
</div>
<% end %>
At this point I am unsure as to how to actually implement this functionality. The user edit page allows the user to change their name, phone and other values but how would I include a new value for the hstore settings?
Inside the form_for from where you are creating users, add your hstore fileds like below :
<%= form_for resource, ...do |f| %>
#...
<%= f.fields_for :settings do |settings| %>
<%= settings.text_field :field1 %>
<%= settings.text_field :field2 %>
<% end %>
#....
<% end %>
Then update your controller strong parameter method like below to permit these new fields.:
def user_params
params.require(:user)
.permit(
:name,
:email,
settings: [ :field1, :field2 ]
)
end
Now, you are done. Open your rails console and try some sample data like
User.create(
settings: { field1: 'data1', field2: 'data2' }
)
I still novice with RoR. My mission is to add examplaries according to the number of exemplary inserted in the form of book, knowing that i have a book table and an examplary table and the relationship in between is a book has_many exemplaries and an exemplary belong_to a book.
This is my attempt:
in books_controller.rb
method create:
def create
#book= Book.new(book_params)
if #book.save
#book.nbr_exemplaires.times do |i|
#exemplaire= Exemplaire.create(book_id: #book.id, state: 0 )
end
flash[:notice]='goood'
redirect_to admin_manage_path
else
flash[:alert]='ouups'
redirect_to root_url
end
end
private
def book_params
params.require(:book).permit(:title, :pages, :resume,:nbr_exemplaires, :has_exemplaires, :author_ids =>[], :subject_ids =>[])
end
book/new.html.erb:
<h1>Add new book</h1>
<%= form_for(#book) do |form| %>
<div> <%= form.label :title %><br>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :pages %><br>
<%= form.number_field :pages %>
</div>
<div>
<%= form.label :resume %><br>
<%= form.text_area :resume %>
</div>
<div>
<p>select author from existing list</p><br>
<%= form.collection_select :author_ids, #authors, :id, :l_name, {:selected => 1}, {:name => 'book[author_ids][]'} %>
</div>
<div>
<p> Select subject from existing list</p><br>
<%= form.collection_select :subject_ids, #subjects, :id, :name, {:selected =>1}, {:name => 'book[subject_ids][]'} %>
</div>
<div>
<%= form.label :has_exemplaires? %>
<%= form.check_box :has_exemplaires,{}, 'Yes', 'No'%>
<div id="expl_details" style="display:none;">
<%= form.label :nbr_exemplaires %> <%= form.number_field :nbr_exemplaires %>
</div>
</div>
<%= form.submit "Submit" %>
<% end %>
<script type="text/javascript">
var checkbox = document.getElementById('book_has_exemplaires');
var details_div = document.getElementById('expl_details');
checkbox.onchange = function() {
if(this.checked) {
details_div.style['display'] = 'block';
} else {
details_div.style['display'] = 'none';
}
};
</script>
Then I would suggest something like this:
class Book < ActiveRecord::Base
has_many :exemplaires
attr_accessor :nbr_exemplaires
after_save :create_exemplaires
private
def create_exemplaires
nbr_exemplaires.times do
self.exemplaires.create()
end
end
If you actually have a column name 'nbr_exemplaires' in your table, you don't need the attr_accessor line. That is only if you won't be saving that as separate value in the DB.
I would use nested attributes, that way rails will create them automatically for you through the same form:
class Book < ActiveRecord::Base
has_many :exemplaires
accepts_nested_attributes_for :exemplaires, allow_destroy: true
This would allow to use the nested form builder in your view:
<%= form_for(#book) do |form| %>
<div><%= form.label :title %><br>
<%= form.text_field :title %>
</div>
<%= form.fields_for :exemplaires do |f| %>
<%= f.text_field :name %>
... more fields
<% end %>
You could then add some javascript to create a multiple nested forms:
<%= link_to_add_fields "Add Exemplarie", f, :answers %>
With this kind of setup, rails will automatically create all the associated objects in the same action with no additional code on the controller/model side. Here is a great railscasts on it:
http://railscasts.com/episodes/196-nested-model-form-revised
If you haven't subscribed, I suggest you do. It's the most useful rails resource I ever used when starting out and it costs only a few $ a month. Good luck!
I have a simple booking system:
class User < ActiveRecord::Base
has_many :appointments
has_many :bookings
end
class Appointment < ActiveRecord::Base
belongs_to :booking
belongs_to :user
end
class Booking < ActiveRecord::Base
belongs_to :course
belongs_to :user
end
etc...
Appointments table:
add_index "appointments", ["booking_id"], name: "index_appointments_on_booking_id"
add_index "appointments", ["user_id"], name: "index_appointments_on_user_id"
create_table "appointments", force: true do |t|
t.integer "booking_id"
t.integer "user_id"
t.boolean "confirmed"
t.boolean "attended"
t.datetime "created_at"
t.datetime "updated_at"
end
the form to create the records for a particular course:
<%= form_for([#course, #booking]) do |f| %>
<% if #booking.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#booking.errors.count, "error") %> prohibited this booking from being saved:</h2>
<ul>
<% #booking.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :course_id, :value => #course.id %>
<div class="field">
<%= f.label "Name" %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label "Description" %><br>
<%= f.text_area :description %>
</div>
<h3>When?</h3>
<div class="field">
<%= f.date_select :start_date %>
</div>
<div class="field">
<%= label_tag "until" %>
<%= check_box_tag(:end_date) %>
</div>
<div class="field" id="end_date_field", style="display:none">
<%= f.date_select :end_date %>
</div>
<div class="field">
<%= f.label "starts at" %><br>
<%= f.time_select :start_time %>
</div>
<div class="field">
<%= f.label "ends at" %><br>
<%= f.time_select :end_time %>
</div>
<div class="field">
<%= label_tag "repeats" %>
<%= check_box_tag(:recurring) %>
</div>
<div class="field" id="recurring_fields" style="display:none">
<%= render 'recur' %>
</div>
<h3>Students</h3>
<div id="students">
<div class="items">
<%= f.nested_fields_for :appointments do |s| %>
<fieldset class="item">
<%= s.collection_select(:user_id, #students, :id, :students_name, :prompt => false) %>
remove
<%= s.hidden_field :_destroy %>
</fieldset>
<% end %>
</div>
Add Student
</div>
<br>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
This all works as planned - the user can make a booking and add many users to a booking.
The problem arises when I want to use the user object of the appointments model:
<%= appointment.user_id %>
the code above shows the id as an integer so proves it was stored correctly BUT
<%= appointment.user %>
comes out blank??
I can't understand why as I'm sure the relationships are set up correctly? Been pulling my hair out with this. Any ideas?
I took your code and created a sample application. I tested it out and think the issue is that you need to add an attribute to your output of the following code:
<%= appointment.user %>
Something like:
<%= appointment.user.name %>
What you are doing currently is that it is referencing the entire user object from the appointment. You can also test this out by loading rails console and doing the following:
appointment = Appointment.first
appointment.user
This will show a User object like the following:
>> appointment.user
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
=> #<User id: 1, name: "test", created_at: "2014-02-28 16:12:15", updated_at: "2014-02-28 16:12:15">
Once you have that you can dig deeper to what you need.
Let me know if this works or you still are having trouble. I am assuming that the output of the appointment.user is in a show context?
Mike Riley
Hoping someone can suggest a fix here. I am fairly new to Rails, and exploring the changes in Rails 4.0. I built this simple recipe book app in Rails 4.0. I have a main model for recipes (name, cook_time, oven_temp, instructions, etc.). Because some recipes may have 5 ingredients and others may have 20, I wanted to break ingredients out in a separate model with a has_many association. So Recipes has_many Ingredients and accepts_nested_attributes_for :ingredients. Here are the models:
recipe.rb
class Recipe < ActiveRecord::Base
belongs_to :user
belongs_to :category
has_many :ingredients, :dependent => :destroy
validates :name, presence: true, length: { maximum: 50 }
accepts_nested_attributes_for :ingredients,
:reject_if => lambda { |a| a[:name].blank? },
:allow_destroy => true
end
ingredient.rb
class Ingredient < ActiveRecord::Base
belongs_to :recipe
end
Reminder: Rails 4.0 no longer uses attr_accessible, but moves assignment into the controller with strong params.
recipes_controller.rb
class RecipesController < ApplicationController
before_action :find_recipe, only: [:show, :edit, :update, :destroy]
before_action :set_current_user, except: [:index, :show]
respond_to :html, :js
...
def edit
end
def update
if #recipe.update_attributes(recipe_params)
redirect_to #recipe
else
render :edit
end
end
...
def find_recipe
#recipe = Recipe.find(params[:id])
end
private
def recipe_params
params.require(:recipe).permit( :id, :name, :category_id, :cook_time, :oven_temp, :calories, :instructions, :notes, :email, ingredients_attributes: [:id, :name])
end
end
I'm using the excellent Cocoon gem from nathanvda to manage nested form fields dynamically, and it works great in 'recipes#new', and saves the information correctly. However, the ingredients fields do not appear in the 'recipes#edit' view! Here is the code for my 'form' and ingredients_fields partials.
_form.html.erb (abbreviated)
<%= form_for #recipe, html: { class: 'form-horizontal' } do |f| %>
<fieldset>
<legend>Main Information</legend>
<div class="field form-group">
<%= f.label :name, "Recipe name", class: "col-lg-2 control-label" %>
<div class="col-lg-5">
<%= f.text_field :name, class: "form-control" %>
</div>
</div>
<div class="field form-group">
<%= f.label :cook_time, class: "col-lg-2 control-label" %>
<div class="col-lg-5">
<%= f.text_field :cook_time, class: "form-control" %>
</div>
</div>
...
</fieldset>
<legend>Ingredients</legend>
<p>Number of ingredients: <%= f.object.ingredients.count unless f.object.ingredients.nil? %></p>
<fieldset id="ingredients">
<% f.fields_for :ingredients do |builder| %>
<%= render 'ingredient_fields', :f => builder %>
<% end %>
<p class="links">
<%= link_to_add_association 'add ingredient', f, :ingredients, { class:"btn btn-primary" } %>
</p>
</fieldset>
<fieldset>
<legend>How to make it</legend>
<div class="field form-group">
<%= f.label :instructions, class: "col-lg-2 control-label" %>
<div class="col-lg-5">
<%= f.text_area :instructions, class: "form-control", rows: "7" %>
</div>
</div>
...
</div>
</fieldset>
<% end %>
_ingredients_fields.html.erb
<div class="nested-fields">
<div class="form-group">
<%= f.label :name, "Ingredient", class: "col-lg-2 control-label" %>
<div class="col-lg-5">
<%= f.text_field :name, class: "form-control" %>
</div>
<%= link_to_remove_association "remove", f %>
</div>
</div>
As I said, pretty simple, removed all error checking and messaging, just the basics. Any ideas about what I am missing? I know there are ingredients in the recipe as I load edit from a show view. Thanks in advance for any advice!
Kevin
Ack. it was as simple as an '=' sign - must have looked at the <% f.fields_for... %> a thousand times and just didn't notice I'd missed adding the <%=.
<%= f.fields_for :ingredients do |builder| %>