I have a form with a date select.
I have the following form:
<%= date_select :user, :birthday, {:start_year => Time.now.year, :end_year => 1910,:order => [:day,:month,:year], :prompt => { :day => 'day', :month => 'month', :year => 'year' }}, { :class => "default" } %>
Validation in my model:
validates :birthday, :if => :should_validate_birthday?,
:presence => {:message => "Please enter your friend's birthdate"},
:date => { :after => Date.civil(1910,1,1), :before => Date.today, :message => "Please enter a valid date"}
Here is an example of what the user submits in the log:
"user"=>{"name"=>"rewrwe", "birthday(3i)"=>"1", "birthday(2i)"=>"", "birthday(1i)"=>"2008", "email"=>""}}
NOTE that the value for the month is blank.
IN the controller I create the user
create
#user = User.new(params[:user])
If #user.save
#go to the confirm page
end
end
No error messages are shown even though the month is missing because for some reason when I try to save the model it converts an empty month to "January" or 01.
This is very frustrating as I don't want users to submit bad data by accident.
How can I stop Rails from doing this and make sure all the date information is submitted?
Separate the three fields with attr_accessor:
class User < ActiveRecord::Base
attr_accessor :birthday_year, :birthday_month, :birthday_date
validates_presence_of :birthday_year, :birthday_month, :birthday_date
# ...
end
And then in your view just have different selects for each of them. That's all the date_select does but it splits them up in the view, rather than in the model, which makes it hard to validate each field.
Related
1 - how can i get the id based on the first or last name selected?
2 - I was able to have it work for first name or last name, but i want to be able to have a full name (based on first or last name) when i start typing. For example when i type ev be able to see Evelin mars, Steve bach, Matt Evans, ....
I have a full name function in my person.rb model.
Is it possible?
view
<%= autocomplete_field_tag 'person_ids[]', '', autocomplete_person_last_name_segments_path, 'data-delimiter' => ',', :multiple => true, :id_element => '#some_element', :placeholder => "type a name" %>
controller
class SegmentsController < ApplicationController
autocomplete :person, :last_name
...
route.rb
resources :segments do
get :autocomplete_person_last_name, :on => :collection
end
person.rb
def full_name
"#{first_name} #{last_name}"
end
UPDATE
I were able to show the full name by adding :extra_data => [:first_name], :display_value => :full_name to the controller and it becomes
controller
class SegmentsController < ApplicationController
autocomplete :person, :last_name, :extra_data => [:first_name], :display_value => :full_name
...
But how do i get the id?
If you look at your AJAX response, you should see that by default, the id is also returned.
I believe that you actually want to use this id. This is what you can do in your view, using :ide_element => #Your_model_id" :
<%= autocomplete_field_tag 'some_label', '', your_autocomplete_path, :id_element => '#yourmodel_id', class: "form-control" %>
<%= f.hidden_field :yourmodel_id, :value => nil %>
You can have a look at the github page of the gem, there is a paragraph dealing with this.
I am using active admin with the money - https://github.com/RubyMoney/money gem. I have some attributes handled by the money gem.
The money gem stores values in cents. When i create an entry with active admin, the correct value is created in DB (5000 for 50.00).
However when i edit an entry, the value is multiplied by 100, meaning that AA display 5000 for an original input of 50.00. If i edit anything with a money attribute, it will multiply by 100. At creation, the value goes through money logic, but at edition, somehow active admin skip that part displaying cents instead of the final monetary value.
Is there a way to use the money gem with active admin?
example :
form :html => { :enctype => "multipart/form-data"} do |f|
f.inputs "Products" do
......
f.has_many :pricings do |p|
p.input :price
p.input :_destroy, :as => :boolean,:label=>"Effacer"
end
f.actions :publish
end
Model :
# encoding: utf-8
class Pricing < ActiveRecord::Base
belongs_to :priceable, :polymorphic => true
attr_accessible :price
composed_of :price,
:class_name => "Money",
:mapping => [%w(price cents), %w(currency currency_as_string)],
:constructor => Proc.new { |cents, currency| Money.new(cents || 0, currency || Money.default_currency) },
:converter => Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError, "Can't convert #{value.class} to Money") }
end
Rails callbacks are very handy for creating a solution to this kind of issue.
I would just use and after_update callback.
Example:
# encoding: utf-8
class Pricing < ActiveRecord::Base
after_update :fix_price
belongs_to :priceable, :polymorphic => true
attr_accessible :price
composed_of :price,
:class_name => "Money",
:mapping => [%w(price cents), %w(currency currency_as_string)],
:constructor => Proc.new { |cents, currency| Money.new(cents || 0, currency || Money.default_currency) },
:converter => Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError, "Can't convert #{value.class} to Money") }
def fix_price
self.price = (self.price/100)
end
end
My problem came from my usage of Money :
composed_of :price,
:class_name => "Money",
:mapping => [%w(price_cents cents), %w(currency currency_as_string)],
:constructor => Proc.new { |price_cents, currency| Money.new(price_cents || 0, currency || Money.default_currency) },
:converter => Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError, "Can't convert #{value.class} to Money") }
I renamed my price in DB by price_cents, and i put it in the class where it was needed in the money declaration. I was using cent where i should have used price, and even then having the money object and the field in DB using the same name doesn't seem to work. In the end the problem was not related to Active Admin.
I have a edit form in Active Admin. I need some field as read only.
My current edit page is like
I need the page look like this
How can this be done. My code for the edit form page is like
form :html => { :enctype => "multipart/form-data" } do |f|
f.inputs "Users" do
f.input :device, :label => 'Device', :as => :select, :collection => DEVICE, :include_blank => false
f.input :current_address, :label => 'Current Address', :as => :string
end
end
Please help.
As Alex said, set to disabled. You could then use css to get the visual you wanted, if you can live with the semantics of that.
The syntax was slightly different for me to get this to work.
in your admin form:
f.input :finish_position, input_html: { disabled: true }
in your CSS active_admin.css
input[disabled="disabled"],
input[disabled] {
background-color: #F4F4F4;
border: 0px solid #F4F4F4 !important;
}
For a cleaner form definition within your ActiveAdmin.register{} block you may as well want to define a "readonly" input type to be used within active admin using formtastic:
Form block syntax is for activeadmin version 1.0.0.pre at 0becbef0918a.
# app/admin/inputs/readonly_input.rb
class ReadonlyInput < Formtastic::Inputs::StringInput
def to_html
input_wrapping do
label_html <<
template.content_tag('div', #object.send(method))
end
end
end
# app/admin/your_model.rb
ActiveAdmin.register YourModel do
# ...
form do |f|
# ...
input :current_address, as: :readonly
# ...
end
end
I was facing the same issue and tried using :disabled but it did not solve my problem as I wanted field value to be included in params object while sending it to the server. When you mark a form input as :input_html => {:disabled => true} , it does not include this field value in params.
So, instead I used :input_html => {:readonly => true} which solved both of my problems:
Does not allow user to edit
Includes the value in params
I hope this will help.
How about this?
form :html => { :enctype => "multipart/form-data" } do |f|
f.inputs "Users" do
f.input :device, :label => 'Device', :as => :select, :collection => DEVICE, :include_blank => false
f.li do
f.label :current_address
f.span f.object.current_address
end
end
end
Try to add , :disabled => true for address input field.
The trick is to use "object". Here is how you should code it:
form :html => { :enctype => "multipart/form-data" } do |f|
f.inputs "Users" do
f.input :device, :label => 'Device', :as => :select, :collection => DEVICE, :include_blank => false
f.label :current_address, f.object.current_address
end
end
Long time reader of Stackoverflow but have never found myself in a position to ask a question (that hasn't already been answered). I guess there's a first time for everything so here it goes...
System Info:
Ruby Version = 1.8.7
Rails Version = 3.2.2
Situation:
We have an application with a user registration system in place. In order to hook up and populate all of our tables correctly, we are utilizing Complex/Nested Forms within the registration view. I actually have the nested forms working perfectly, everything is being populated as it should, its awesome really.
Here is the problem: I need to set one of the value of one of the nested attributes AFTER the form post but BEFORE the records are saved.
Here is a quick example so you can see what I'm talking about a little bit better:
A user registers with our site. When they register a record is created in the Users data table. Each user is also classified as a team_mate (join table) and assigned to their very own individual team (at first). But, a 'team' (table) also has an 'alias' field in it which, on the initial creation of the user we would like to set to the users first name (without having to have them enter their first name into an 'alias' field on the form).
So, I guess the question would be: How to I manually set the value of a nested attribute after the form post and before the records are saved to the database?
A (simplistic) example of the table schema looks is as follows:
Users (id, first_name, last_name, created_at, updated_at)
Team_mates(id, user_id, team_id, created_at, updated_at) - join table
Teams(id, alias, created_at, updated_at)
Models:
User.rb
class User < ActiveRecord::Base
has_many :team_mates, :dependent => :destroy
has_many :teams, :through => :team_mates, :foreign_key => :team_id
accepts_nested_attributes_for :team_mates, :allow_destroy => true
before_save :set_defaults
private
def set_defaults
#want to set :users => :team_mates_attributes => :team_attributes => :alias to #user.first_name here
# Would prefer to handle this here instead of in the controller.
end
end
Team.rb
class Team < ActiveRecord::Base
has_many :team_mates, :dependent => :destroy
has_many :users, :through => :team_mates, :foreign_key => :user_id
end
Team_mate.rb
class TeamMate < ActiveRecord::Base
belongs_to :user
belongs_to :team
accepts_nested_attributes_for :team, :allow_destroy => true
end
Controller
Users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
#user.emails.build(:is_default_email => 1)
#user.build_login
#user.team_mates.build.build_team(:alias => 'Clinton444', :created_at => Time.new, :updated_at => Time.new)
respond_to do |format|
format.html
format.json { render :json => #match }
end
end
def create
#user = User.new(params[:user])
#user.attributes = ({ "user" => { "team_mates" => { "team" => { "alias" => #user.first_name } } } }) #--this doesn't work...
#user.attributes = ({ :user => { :team_mates => { :team => { :alias => #user.first_name } } } }) #--neither does this...
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.json { render :json => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.json { render :json => #user.errors, :status => :unprocessable_entity }
end
end
end
View
new.html.haml
= form_for(#user, :html => {:class => 'form-horizontal'}) do |f|
- if #user.errors.any?
.alert
%h2
= pluralize(#user.errors.count, "error")
prohibited this post from being saved:
%ul
- #user.errors.full_messages.each do |msg|
%li
= msg
%fieldset
.control-group
= f.label :first_name, :class => "control-label"
.controls
=f.text_field :first_name, :class => "span8"
.control-group
= f.label :last_name, :class => "control-label"
.controls
=f.text_field :last_name, :class => "span8"
= f.fields_for :emails do |e|
=e.hidden_field :is_default_email, :class => "span8"
.control-group
= e.label :email, :class => "control-label"
.controls
=e.text_field :email, :class => "span8"
= f.fields_for :team_mates do |tm|
= tm.fields_for :team do |t|
=t.hidden_field :alias, :class => "span8"
=t.hidden_field :created_at, :class => "span8"
=t.hidden_field :updated_at, :class => "span8"
= f.fields_for :login do |e|
.control-group
= e.label :user_login, :class => "control-label"
.controls
=e.text_field :user_login, :class => "span8"
.control-group
= e.label :password_encrypted, :class => "control-label"
.controls
=e.text_field :password_encrypted, :class => "span8"
.control-group
.controls
=f.submit :class => 'btn btn-primary btn-medium'
And finally
Rails server output on form post
Parameters: {"user"=>{"team_mates_attributes"=>{"0"=>{"team_attributes"=>{"created_at"=>"Wed Jun 06 09:52:19 -0600 2012", "alias"=>"asfs444", "updated_at"=>"Wed Jun 06 09:52:19 -0600 2012"}}}, "first_name"=>"lkjlkjlsdfslkjeowir", "last_name"=>"ouisodifuoixv", "emails_attributes"=>{"0"=>{"is_default_email"=>"1", "email"=>"lpisfsopf#psflsjdk.com"}}, "login_attributes"=>{"user_login"=>"lkjsdfooiusfd", "password_encrypted"=>"[FILTERED]"}}, "utf8"=>"✓", "commit"=>"Create User", "authenticity_token"=>"CQLQ93/0VlncSzMlmtLPHgaVrrvjuHFN+lN6CYCsiR8="}
After looking at the models you might be wondering where emails/logins are coming from. They're built within the model on our system, but are not really part of this question so I omitted the code for them. They are working, so the problem isn't on that side.
Check http://archives.ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes
To support both the creation of new objects and the editing of
existing ones we have to use an array of hashes for one-to-many
associations or a single hash for one-to-one associations. If no :id
property exists then it is assumed to represent a nested model to
create.
Not 100% sure.. I haven't used\tested it before, but this should give you an idea
#user.teams.each do |team|
team.team_mates do |team_mate|
# To edit existing
team_mate.team_attributes = [ { :id => team.id, :alias => #user.first_name } ]
# To create new
team_mate.team_attributes = [ { :alias => #user.first_name } ]
team_mate.save
end
end
User has two addresses shipping(:address_type=0) and billing(:address_type=1)
User form with 2 classic nested forms for each address type are generated square times every submit and failed validation.
Models:
class User < ActiveRecord::Base
has_many :addresses, :dependent => :destroy
accepts_nested_attributes_for :addresses
validates_associated :addresses
end
class Address < ActiveRecord::Base
belongs_to :user
validates :user, :address_type, :first_name, :last_name, :street
end
Controller
class UsersController < ApplicationController
public
def new
#user = User.new
#shipping_address = #user.addresses.build({:address_type => 0})
#billing_address = #user.addresses.build({:address_type => 1})
end
def create
#user = User.new(params[:user])
if #user.save
#fine
else
render => :new
end
end
Uncomplete Form
=form_for #user, :html => { :multipart => true } do |ff|
=ff.fields_for :addresses, #shipping_address do |f|
=f.hidden_field :address_type, :value => 0
=ff.fields_for :addresses, #billing_address do |f|
=f.hidden_field :address_type, :value => 1
=ff.submit
The form should look like this:
=form_for #user, :html => { :multipart => true } do |ff|
=ff.fields_for :addresses do |f|
Nothing else.
Addressess is already a collection, so you should have just one rendering of it.
Also that ":addresses, #shipping_address" makes it to render addresses AND shipping address, even if it's included in #user.addresses.
The addressess built in new action will show there because they are in the addresses collection.
EDIT:
If you need only these two addresses, you can sort it and pass it to fields_for directly:
=form_for #user, :html => { :multipart => true } do |ff|
=ff.fields_for ff.object.addresses.sort{|a,b| a.address_type <=> b.address_type } do |f|
That should do it.
Surprised? I guess not but I was. I found it am I correct? And its stupid and simple.
There is no #shipping_address nor #billing_address when validation fails and rendering the new action (the form) again. But #user has already 2 addresses builded and nested form behave correctly to render each twice for first time failed validation.
def create
#user = User.new(params[:user])
if #user.save
#fine
else
#user.addresses.clear
#user_address = #user.addresses.build({:address_type => 0})
#user_address.attributes = params[:user][:addresses_attributes]["0"]
#billing_address = #user.addresses.build({:address_type => 1})
#billing_address.attributes = params[:user][:addresses_attributes]["1"]
render => :new
end
end