I'm really new to Rails and I'm using v6.1.4. In my application I'm using I18n localization. At other places for view-only label dates(not using BIP) I use
<%=l #client.active_date.to_date %>
and it works. However, in Best-In-Place there is a different syntax.
<span class="date-container"><%= best_in_place task, :due_date, :as => :date, class: 'hyperlink' %></span>
This causes the dates to be displayed in the yyyy-mm-dd format throughout. I want the dates to be formatted according to the locales currently in my application. I'm using the JQuery datepicker, but it is activated after I click on the date, and the format is changed to what I have set on the jquery setDefaults option.
How do I tackle this?
I think this post addresses your question.
You could add a display_as: :formatted_date to your method call and define that formatted_date method in your model. From there you should be able to format dates as needed.
Related
When a user creates a vacancy I want them to be able to save either 1 range of dates or multiple separate dates.
So I have 2 models, Vacancy and Vacancyschedule.
Vacancyschedule includes vacancy_id start_date end_date start_hour end_hour (to save a range) and when it is multiple seperate dates, I just want to leave the end_date empty and have multiple entries and combine them through my vacancy_id.
This is my code in my view:
... other code to ask for vacancy params, nothing special, nothing broken ...
#Code to create entry in vacancyschedule
<%= t.simple_fields_for :vacancyschedule do |p| %>
<%= p.input :start_date, :as => :date_picker, :label => false%>
<%= p.input :start_hour, as: :time, default: Time.parse('09:00')%>
<%= p.input :end_hour, as: :time, default: Time.parse('17:00')%>
<% end %>
And then I have some javascript that adds another exact copy one of those blocks when a user wants to add a second seperate date.
Now for my question:
The format in which they are passed is very strange. As you can see I ask for :start_date, :start_hour and :end hour and this is what I get:
{"name"=>"", "street"=>"", "description"=>"", "skill_id"=>"",
"jobtype_id"=>"", "wage"=>"", "vacancyschedule"=>
{"start_date"=>"27/10/15", "end_date"=>"", "start_hour"=>"",
"end_hour"=>"", "start_hour(1i)"=>"2015", "start_hour(2i)"=>"10",
"start_hour(3i)"=>"26", "start_hour(4i)"=>"21",
"start_hour(5i)"=>"00", "end_hour(1i)"=>"2015", "end_hour(2i)"=>"10",
"end_hour(3i)"=>"26", "end_hour(4i)"=>"17", "end_hour(5i)"=>"00"},
"vacancyscheduele"=>{"start_date"=>"", "start_hour(1i)"=>"2015",
"start_hour(2i)"=>"10", "start_hour(3i)"=>"26", "start_hour(4i)"=>"09",
"start_hour(5i)"=>"00", "end_hour(1i)"=>"2015", "end_hour(2i)"=>"10",
"end_hour(3i)"=>"26", "end_hour(4i)"=>"17", "end_hour(5i)"=>"00"},
"tag_list"=>""}, "radio"=>"on", "commit"=>"Create Vacancy",
"controller"=>"vacancies", "action"=>"create", "employer_id"=>"2"}
From what I can see is that they are passed in very different variables and incomplete (not all dates are passed).
Does anyone have any experience with this issue? How would I either grab these elements or how do I prevent them from being pushed in that format?
Eeeehhh, I hate nested forms. I always go back to the basics when I have to deal with them. Checkout these fine railscasts. The second one I think is better aimed at your problem but the first one helps understand it:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
At the end of the day, you need to know what your html is generating and make sure it is what Rails is expecting in the backend. Also, you must account for the nested objects at load and save time. Your HTML as it stands, is not generating parseable objects as far as Rails is concerned. On the other hand, it is hard to deal with the crap date/time picker for Rails. I usually like to put in something that generates a better date picker. One that generates one string that is easily maps to a database field. If you are using bootstrap, I'd use the bootstrap datepicker gem.
Rails ~ 4.2.1
Mongoid ~ 4.0.2
in _form.html.erb, the code looks like this:
<%= form_for #workout %>
<%= f.label :date%>
<%= f.datetime_select :date>
<%= f.submit %>
<% end %>
in Workout.rb, the code looks like this:
class Workout
include Mongoid::Document
field :date, type: DateTime
end
I naively thought The View could send {"workou[date]" => "2015/06/20 10:10"} to the Controller, but actually, it sent {"workout[date(1i)]" => "2015", workout[date(2i)] => "06", ... workout[date(5i)] => "10"}. In the end, in Mongo workout collection, it stored the time separately.
I want Mongo DB to store them in one field, type as DateTime. How can I achieve that?
You ask for Datetime, but maybe my answer (using Date type) can lead you the way too. Let's see.
This answer suggests the Best way to store date/time in mongodb. That is if you can you should choose native JavaScript Date objects.
The way I solve my issue (with Date object) was as follows.
I use the Date field type in my models. E.g. the Event model has two date types, from_date and end_date.
In the controller (e.g. new method) you can use the Date object as usual, no special treatment in my case. I guess for you it is the same. E.g. Date.current or Date.current+3.days.
Note the create method does not specifically manipulate Date objects. The parameters from the web view form are taken as is.
In the view I use the date_field helper. It is an input field of type date. Note that currently Google Chrome supports the date field type, so you will see a date picker pop up. Other browsers don't support the field yet, so it is a simple input field for them. I use jQuery Datepicker in case the browser does not support the input date type field, e.g.
$(document).ready(function(){
...
if ( $('[type="date"]').prop('type') !== 'date' ) {
$('input[type="date"]').datepicker({dateFormat: "yy-mm-dd"});
}
...
});
So in your case maybe you could do something analogue to that
solution. Probably there is no datetime_field helper method, but a
simple input field could do and you use JS to ensure you are saving a
native JS Date/Datetime object for the corresponding field.
In Rails' forms, date select drop-downs are easy:
<%= f.label :my_date %>
<%= f.date_select :my_date %>
Then I get 3 select drop-downs for the year, month and day. Is there an easy way to add HTML between these select elements without using JavaScript? I want to be able to style and label them nicer.
Convert it to a text field and add a JQuery datepicker or similar.
Theres a good guide here, http://railscasts.com/episodes/213-calendars
You can easily style them with CSS as each part of date_select is postfixed with '_1i', '_2i' etc . For a date_select with id 'my_date' you can use
#my_date_1i // the year
#my_date_2i // the month
#my_date_3i // the day
Hope this helps
Cheers
in my Rails 3.1, ruby 1.9.2 application, I've an Object 'Post' that has several fields such as id,created_at,updated_at,name,description.... In my view I want show post.created_at field filtered by date: I explain better:
In my view I'd want something such as:
but html must render something such as: 2011-30-06 and not 2011-30-06 11:04 am
in other words I want display only date and not hour, how can I do?
You should use strftime to format correctly the date (here's the documentation)
It should look like this <%= post.created_at.strftime("%Y-%M-%d) %>
I'd suggest to create method display_date in application_helper.rb, don't forget about DRY!
def display_date(input_date)
input_date.strftime("%Y-%M-%d")
end
I am working on a Rails 3 project where there is place for date input within a form. The text field with the date uses a date picker so there is no concern about the date being entered in a wrong format, however the date is being displayed in the :db format (e.g. 2010-01-21).
(Note: this is specifically in form fields - e.g. <%= f.text_field :publish_date %>, which should automatically use :default format, and shouldn't need to be provided with a value)
I have tried adding in a customized locale which has the following date configuration:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d/%m/%Y"
short: "%b %d"
long: "%B %d, %Y"
And then setting my locale to this (config.i18n.default_locale = "en-AU") however this doesn't seem to take and its becoming quite frustrating.
The app will eventually support a number of locales, so setting up an initializer to override the date formats at application startup isn't really suitable, and I know that this should work - I'm guessing I've missed something here.
The locale file is: config/locales/en-AU.yml and in my application.rb I am including:
config.i18n.load_path += Dir[Rails.root.join("config", "locales", "*.yml").to_s]
config.i18n.default_locale = "en-AU"
in my application.rb file.
When displaying a date, you can use I18n.l
So you would do :
I18n.l #entry.created_at
And if you want to change it's format :
I18n.l #entry.created_at, :format => :short
The internationalization rails guide is documenting that.
#damien-mathieu has a solid answer for displaying localized dates with I18n.localize, and his comment raises an important caveat: this breaks form text inputs. Since then, rails gives us a nice solution.
As of Rails 5, you can use the
Rails attributes API to customize how user input is transformed into a model or database value. Actually, it was available in Rails 4.2, just not fully documented.
Through Sean Griffin's considerable efforts, all models' types are now defined as ActiveRecord::Type objects. This defines a single source of truth for how an attribute is handled. The type defines how the attribute is serialized (from a ruby type to a database type), deserialized (from a database type to a ruby type) and cast (from user input to a ruby type). This is a big deal, because messing with this used to be a minefield of special cases developers should avoid.
First, skim the attribute docs to understand how to override an attribute's type. You probably need to read the docs to understand this answer.
How Rails Transforms Attributes
Here's a quick tour of the Rails Attributes API. You can skip this section, but then you won't know how this stuff works. What fun is that?
Understanding how Rails handles user input for your attribute will let us override only one method instead of making a more complete custom type. It will also help you write better code, since rails' code is pretty good.
Since you didn't mention a model, I'll assume you have a Post with a :publish_date attribute (some would prefer the name :published_on, but I digress).
What is your type?
Find out what type :publish_date is. We don't care that it is an instance of Date, we need to know what type_for_attribute returns:
This method is the only valid source of information for anything related to the types of a model's attributes.
$ rails c
> post = Post.where.not(publish_date: nil).first
> post.publish_date.class
=> Date
> Post.type_for_attribute('publish_date').type
=> :date
Now we know the :publish_date attribute is a :date type. This is defined by ActiveRecord::Type::Date, which extends ActiveModel::Type::Date, which extends ActiveModel::Type::Value. I've linked to rails 5.1.3, but you'll want to read the source for your version.
How is user input transformed by ActiveRecord::Type::Date?
So, when you set :publish_date, the value is passed to cast, which calls cast_value. Since form input is a String, it will try a fast_string_to_date then fallback_string_to_date which uses Date._parse.
If you're getting lost, don't worry. You don't need to understand rails' code to customize an attribute.
Defining a Custom Type
Now that we understand how Rails uses the attributes API, we can easily make our own. Just create a custom type to override cast_value to expect localized date strings:
class LocalizedDate < ActiveRecord::Type::Date
private
# Convert localized date string to Date object. This takes I18n formatted date strings
# from user input and casts them back to Date objects.
def cast_value(value)
if value.is_a?(::String)
return if value.empty?
format = I18n.translate("date.formats.short")
Date.strptime(value, format) rescue nil
elsif value.respond_to?(:to_date)
value.to_date
else
value
end
end
end
See how I just copied rails' code and made a small tweak. Easy. You might want to improve on this with a call to super and move the :short format to an option or constant.
Register your type so it can be referenced by a symbol:
# config/initializers/types.rb
ActiveRecord::Type.register(:localized_date, LocalizedDate)
Override the :publish_date type with your custom type:
class Post < ApplicationRecord
attribute :publish_date, :localized_date
end
Now you can use localized values in your form inputs:
# app/views/posts/_form.html.erb
<%= form_for(#post) do |f| %>
<%= f.label :publish_date %>
<%= f.text_field :publish_date, value: (I18n.localize(value, format: :short) if value.present?) %>
<% end %>
check out the delocalize gem, it might help you out some.
https://github.com/clemens/delocalize
http://www.railway.at/articles/2009/05/03/new-plugin-delocalize/
What I found to be the best solution is this:
I localize date formats in my locale file like you do
In my forms I localize the date by setting the value directly
<%= f.text_field :publish_date, :value => (#model.publish_date.nil? ? nil : l(#model.publish_date)) %>
It is not perfect sadly, but at least this way I can use my form for both new and existing records. Also the app will stay compatible with multiple locales compared to changing the default format with initializers. If you fully want to comply with DRY you could always write a custom helper.
You could override your getters for the :publish_date attribute.
i.e. in your model:
def date( *format)
self[:publish_date].strftime(format.first || default)
end
and in your view you could do either
#income.date("%d/%m/%Y")
or
#income.date
This would cause strftime to use the passed format string unless it was nil, in which case it would fall back to your default string.
Note: I used the splat operator to add support for getting the date without an argument. There may be a cleaner way to do that.