There is the following code:
.row
= f.label :start_time
= time_select :model, :start_time, { minute_step: 30 }
.row
= f.label :end_time
= time_select :model, :end_time, { minute_step: 30 }
time_select generates 2 selects for each copy( 1 for hours and 1 for minutes). As result we have 'model' hash in params with 'start_time(1i)', ... 'start_time(5i)' fields (the same for end_time). I have the following questions:
How can I create a new Time object from this hash?
Is it possible to create only one select for each time_select?
Thanks
How can I create a new Time object from this hash?
You can let Rails do the work for you:
= f.time_select(:start_time, minute_step: 30)
When you call update_attributes on your model in the controller, Rails will create the Time object and assign it to the model.
If you use Rails 4, be sure to whitelist :start_time and :end_time using the strong parameters.
Is it possible to create only one select for each time_select?
By default, Rails can't do this. However, you can use the combined_time_select gem to do it for you:
f.time_select(:end_time, combined: true, minute_step: 30)
Be sure to restart your Rails server after installing this gem.
These helpers are optimised for use with a ActiveRecord model and its attributes. If you have the columns start_time and end_time, you should not need to worry about creating the Time object from the passed params yourself.
What are you specifically trying to achieve?
Related
How can you convert a mysql datetime field into two form fields (1) date only, (2) time only, and combine both fields back into datetime format on form submit?
This would allow the use of the following gems, but store the dates in a single datetime field:
gem 'bootstrap-datepicker-rails'
gem 'bootstrap-timepicker-rails'
Thanks in advance!
Found the solution with help from #Althaf
Added virtual attributes to model.rb
Used before_save callback to convert back to datetime.
before_save :convert_to_datetime
def sched_date_field
sched_date.strftime("%d/%m/%Y") if sched_date.present?
end
def sched_time_field
sched_time.strftime("%I:%M%p") if sched_time.present?
end
def sched_date_field=(date)
# Change back to datetime friendly format
#sched_date_field = Date.parse(date).strftime("%Y-%m-%d")
end
def sched_time_field=(time)
# Change back to datetime friendly format
#sched_time_field = Time.parse(time).strftime("%H:%M:%S")
end
def convert_to_datetime
self.sched_time = DateTime.parse("#{#sched_date_field} #{#sched_time_field}")
end
Using Rails 4, needed to add sched_date_field and sched_time_field to strong params in controller.rb
Here are the fields in _form.html.erb
<%= f.label :sched_date_field, "Scheduled Date" %>
<%= f.text_field :sched_date_field, :class => "datepicker" %>
<%= f.label :sched_time_field, "Scheduled Time" %>
<%= f.text_field :sched_time_field, :class => "timepicker" %>
You can use date_time_attribute gem:
class MyModel < ActiveRecord::Base
include DateTimeAttribute
date_time_attribute :scheduled_at
end
It will allow you to set schedule_at_date and scheduled_at_time separately. Once attributes are set, values will be combined into schedule_at.
You could use virtual attributes See this Railscast and if you have a pro subscription the revised one.
Basically in the view you would the following
<%= f.label :date_field %>
<%= f.text :date_field %>
<%= f.label :time_field %>
<%= f.text :time_field %>
Your database would still keep a field which I'll call full_date
Now in your model you would have to define the above 2 fields as follows.
def date_field # What this returns will be what is shown in the field
full_date.strftime("%m-%d'%y") if full_date.present?
end
def time_field
full_date.strftime("%I:%M%p") if full_date.present?
end
def time_field=(time)
full_date = DateTime.parse("#{date_field} #{time_field})
end
Since it looks like you are using Rails 4, you'll have to permit date_field and time_field in your strong parameters.
Alternatively, I set up a solution in the controller that does all the datetime conversions before the object gets created, because changing the data in the model impacted all my tests and validations. "Event" is the object I'm creating here with the datetime values being assigned to it.
#In the controller:
def convert_to_datetime_and_assign(event, params)
date_field = Date.parse(params[:date_field]).strftime("%Y-%m-%d")
start_time_field = Time.parse(params[:start_time_field]).strftime("%H:%M:%S")
end_time_field = Time.parse(params[:end_time_field]).strftime("%H:%M:%S")
event.start_time = DateTime.parse("#{date_field} #{start_time_field}")
event.end_time = DateTime.parse("#{date_field} #{end_time_field}")
event
rescue ArgumentError
event.errors.add(:start_time, :invalid, message: "Date or time was invalid")
event
end
in the create and update controller methods I called the method above:
#event = convert_to_datetime_and_assign(#event, event_params)
I added fields for date_field, start_time_field and end_time_field in my forms for creating/updating "events". And in the model I added an accessor to be able to access those values.
attr_accessor :date_field, :start_time_field, :end_time_field
I have a "Word" model that has 3 string variables: "word_a" , "word_b" , "word_ab".
A form in my view collects the values for "word_a" and "word_b":
<%= f.text_field :word_a %>
<%= f.text_field :word_b %>
What is the best way to save the value for "word_ab" which will be made automatically from a combo of "word_a" and "word_b"?
The way Im doing it now seems really really wrong. After submitting the first form, I have the controller redirect to another edit page with a 'word_ab' form that has a value that combines 'word_a' and 'word_b'.
<%= f.text_field :word_ab, :value => #word.word_a+"_"+#word.word_b %>
The user then has to resubmit the form to save 'word_ab' into the database. Can't I do this is a controller?
Are you sure you want to save concated value to db? What about editing?
If you still want to do it - best idea is to add attr_accessor in model
attr_accessible :word_a, :word_b
attr_accessor :word_a, :word_b
First line allows yo perform mass assign, second one creates setter and getter methods.
Then, still in a model do
before_validation(:on => :create) do
self.word_ab = word_a + word_b
end
You may perform this on before_save as well, and you may validate word_a and word_b separately with regular validators.
Pro tip: create getter method that returns concated string
def word_ab
self.word_a + self.word_b
end
Associated models
class User
has_one :profile_picture
def word_ab
self.profile_picture.url + self.word_a + self.word_b
end
end
Is it possible to somehow use the :selected option that you'd use on a normal select view helper with the grouped_collection_select function? I'd like to set the value that gets pre-selected in my list. I've tried passing in :selected as an option with no luck!
Here's some code snippts of my tests:
grouped_collection_select 'user[subscription_attributes]', :subscription_plan_id, Trade.order(:name).all, :subscription_plans, :name, :id, :display_name, { :include_blank => true, :selected => 5 }
grouped_collection_select 'user[subscription_attributes]', :subscription_plan_id, Trade.order(:name).all, :subscription_plans, :name, :id, :display_name, :include_blank => true, :selected => 5
Neither version works. No selected is set. I'm using this to set a value for a nested model. I'm using the railscasts dynamic select list methods: http://railscasts.com/episodes/88-dynamic-select-menus-revised
I couldn't get formtastic to play nicely with the group selects so I had to do it by hand but I don't keep this value selected when a user fails validations. I'd like to keep this set when they fix validation errors.
I just ran across the same problem and solved it using the option_groups_from_collection_for_select helper and the select helper documented at: http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html.
The first step is to create the grouped options. Taking your example, it should look like:
<% options = option_groups_from_collection_for_select(Trade.order(:name).all,
:subscription_plans, :name, :id, :display_name, 5) %>
Then I created the select object like:
<%= select('user[subscription_attributes]', :subscription_plan_id, options,
include_blank: true) %>
You could write it all out in one line, I just broke out the options into a separate variable to illustrate the two different methods.
Maybe too late, but the API documentation of grouped_collection_select states:
'The value returned from calling method on the instance object will be selected.'
So, you don't even have to specify a :selected option, since Rails will automatically select based on the current value of your attribute.
If subscription_plan_id has value 5, then that's what will be selected.
If that's supposed to be a default value, then you can set it with an after_initialize in your model.
Actually you need to send in options include_blank for example
<%= grouped_collection_select :id, model.all, options = {:include_blank => 'Selecione'}%>
I'm not quite sure what the correct terms are, but what I'm trying to do is in a form (preferably using the simple_form gem) have one of the inputs, :maximum, use both a text field and select box. The user would type in the text box a number, and then select from a dropdown box of hours, days, or months. So 21 days, 3 months, 3 hours, etc. When the form was submitted I would convert that to days and store it in the database. I know how to change the input type in simple_form, but is it possible to have two inputs for one variable?
Sure :) Here is my idea:
First, you define accessors in your user model:
attr_accessor :thing, :another_thing, :and_another_thing
Then in your view, 'inside' form_for helper, you could write for example:
<%= form.input :thing, :as => :boolean %>
<%= form.input :another_thing, :as => :text %>
...or whatever you want. (Note: I am using formtastic here. You should consider using Rails methods if you're not using formtastic gem. )
Finally, you define a callback in you user model:
before_create :build_my_fancy_record
def build_my_fancy_record
self.storage_field = "#{thing} #{another_thing}"
end
I have created a custom date_Select field using 3 separate select fields:
<%= f.select :day, options_for_select(User::DAYS), :include_blank => "Day:" %>
<%= f.select :month, options_for_select(User::MONTHS), :include_blank => "Month:" %>
<%= f.select :year, options_for_select(User::YEAR_RANGE), :include_blank =>"Year:" %>
In my User.rb (Model) I have this validation rule and also using validates_timelessness gem:
MONTHS = ["January", 1], ["February", 2]..etc
DAYS = ["01", 1], ["02", 2], ["03", 3]..etc
START_YEAR = Time.now.year - 111
END_YEAR = Time.now.year
YEAR_RANGE = START_YEAR..END_YEAR
validates :birthday, :timeliness => {:on_or_before => lambda { Date.current }, :type => :date, :on_or_before_message => "Select a valid birthday"}
I have created some tests which work perfectly fine with the date_select that comes with rails but that date_select is buggy which is why I opted for a custom one. My only issue now is I wish to get day, month and year to work with my :birthday symbol. How do I combine all 3 so that my :birthday symbol can use the select data? If that makes sense...
The date_select would have been perfect but it lets users submit a form without the yea being filled out and if a users chooses 1 for a day and clicks submit it will automatically select january. I haven't found a way round that.
So I'm using 3 separate select fields which I want to combine and make work with :birthday just like date_select did.
Help is appreciated.
i would recommend using a date-select pop-up.
There are several gems available. I have used the one detailed in http://www.rubyinside.com/calendar-date-select-a-lightweight-prototype-based-datetime-picker-for-rails-developers-573.html with success.
Once you have a real date field you'll be in a better position to perform date type validations including ranges and presence that you can be confident in worrectly with that type of data.
The select_date isn't buggy, you need to perform the necessary data validation on your end so an empty year isn't allowed. Validation is designed to let you specify what data is allowed into your app. Only you can be the gatekeeper of what is considered 'valid'.