How to set custom field name ? - ruby-on-rails

I am trying to use a collection_select for state just to filter cities, but I don't want to save the state_id in db. My "college" model has only city and college field. That's why it's throwing while instantiating :state_id I believe, I am new to rails. I am not able to figure it out. :(
<%= form_for #college do |f| %>
<div class="form-group">
<%= f.label "Select city"%>
<%= f.collection_select(:state_id, State.all, :id, :state, {}, {class: "form-control"})%>
</div><br>
<div class="form-group">
<%= f.label :city_id, "Select city"%>
<%= f.collection_select(:city_id, City.all, :id, :city, {}, {class: "form-control"})%>
</div><br>
<div class="form-group">
<%= f.label :college, "college Name"%>
<%= f.text_field :college, class: "form-control", placeholder: "Enter city name", required: true%>
<br>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary"%>
</div>
<% end %>

just define state_id attribute accessor in your college.rb model :
attr_accessor :state_id

inside your model you should define state_id as attr_accessor like :-
class College
attr_accessor :state_id
end

Related

form_for with same attributes for different year

trying to implement a form_for with different years having the same input.
I have two tables: Position (empty one) and PositionSteps (with year / company_name / position_title)
class Position < ApplicationRecord
belongs_to :user
has_many :position_steps, dependent: :destroy
end
class PositionStep < ApplicationRecord
belongs_to :position
validates_uniqueness_of :year
end
For exemple, a person have to indicate the the position and the company he works for.
The first line is based on current information :
2020 Google Sales (autocomplete with current info - that can be changed)
2021 Google Head of sales
2022 Facebook Head of marketing
<%= form_for #score do |f| %>
<div class="form-group">
<%= f.date_field :year, value: #default_year, class: "form-control" %>
<%= f.text_field :company_name, value: #default_company_name, class: "form-control" %>
<%= f.text_field :position_title, value: #default_position_title, class: "form-control" %>
</div>
<div class="form-group">
<%= f.date_field :year, class: "form-control" %>
<%= f.text_field :company_name, class: "form-control" %>
<%= f.text_field :position_title, class: "form-control" %>
</div>
<div class="form-group">
<%= f.date_field :year, class: "form-control" %>
<%= f.text_field :company_name, class: "form-control" %>
<%= f.text_field :position_title, class: "form-control" %>
</div>
<%= f.submit "Validate", class: 'btn' %>
<% end %>
Not really sure about the way to implement my form in my edit page and ta save the data the user will add in my new table
Thanks for your help

Failed to save the new associated seo_setting on has_one association

I have 2 models:
class Page < ApplicationRecord
enum page_type: STATIC_PAGE_TYPES, _suffix: true
has_one :seo_setting
accepts_nested_attributes_for :seo_setting, update_only: true
validates :title, :subtitle, length: { maximum: 50 }
validates :page_type, uniqueness: true
def to_param
"#{id}-#{page_type}".parameterize
end
end
and
class SeoSetting < ApplicationRecord
mount_uploader :og_image, SeoSettingsOgImageUploader
belongs_to :page
validates :seo_title, :seo_description, :og_title, :og_description, :og_image, presence: true
end
My Page objects are created from the seeds.rb file, and when I want to edit them, I get an error: Failed to save the new associated seo_setting.
In the form I have this:
<div class="card-body">
<%= form_for([:admin, #page]) do |f| %>
<%= render 'shared/admin/error-messages', object: #page %>
<div class="form-group">
<%= f.label :title, t('admin.shared.title') %>
<%= f.text_field :title, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :subtitle, t('admin.shared.subtitle') %>
<%= f.text_field :subtitle, class: 'form-control' %>
</div>
<h3>SEO Settings</h3>
<%= f.fields_for :seo_setting, f.object.seo_setting ||= f.object.build_seo_setting do |form| %>
<div class="form-group">
<%= form.label :seo_title, t('admin.shared.seo_title') %>
<%= form.text_field :seo_title, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :seo_description, t('admin.shared.seo_description') %>
<%= form.text_area :seo_description, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :og_title, t('admin.shared.og_title') %>
<%= form.text_field :og_title, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :og_description, t('admin.shared.og_description') %>
<%= form.text_area :og_description, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :og_image, t('admin.shared.og_image') %>
<div class="row">
<div class="col-lg-12">
<%= image_tag(form.object.og_image.url, style: 'width: 100px') if form.object.og_image? %>
</div>
</div>
<%= form.file_field :og_image %>
<%= form.hidden_field :og_image_cache %>
</div>
<% end %>
<div class="form-group">
<%= f.submit t('admin.actions.submit'), class: 'btn btn-success' %>
<%= link_to t('admin.actions.cancel'), admin_page_path(#page) , class: 'btn btn-default' %>
</div>
<% end %>
</div>
If I remove validations from my SeoSetting model, everything is working. It seems Rails doesn't like this part: f.object.build_seo_setting, because it creates a record in my database. Any ideas of how can I solve this issue? Thanks ahead.
Just had to change this line:
<%= f.fields_for :seo_setting, f.object.seo_setting ||= f.object.build_seo_setting do |form| %>
for this one:
<%= f.fields_for :seo_setting, #page.seo_setting.nil? ? #page.build_seo_setting : #page.seo_setting do |form| %>
Looks as if the problem lies here:
accepts_nested_attributes_for :seo_setting, update_only: true
in that you only allow the updating of the seo_setting on update.
Then, when you use this code:
f.object.seo_setting ||= f.object.build_seo_setting
You're falling back to a new seo_setting if the associated object is missing.
For this to work, you'll need to either remove the update_only: true, or only render the fields for the association if the seo_setting already exists.
Accepted answer, a conditional inside fields_for line, by OP, works also with polymorphic association and nested form (fields_for). Thnx Alex.

How to display a form field whose role is of enum datatype and is stored as integer in the database. The user can't change the role

My profile role is created and when the user logs in the profile controller's edit action view will be displayed. I want to show the role that is assigned to user in the edit action and the user cannot change the role. My edit.html.erb file is given as:
<div class="row">
<div class="col-md-6 offset-md-3">
<h3>Profile</h3>
<%= form_for(#profile) do |f| %>
<div class="form-group">
<%= f.label :first_name %><br />
<%= f.email_field :first_name, autofocus: true, class: "form-control"%>
</div>
<div class="form-group">
<%= f.label :last_name %><br />
<%= f.password_field :last_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :role %><br />
<%= f.select(:role, [['User', 'user'], ['Vip', 'vip'], ['Admin', 'admin']]) %>
</div>
<div class="actions form-group">
<%= f.submit "Submit", class: 'btn btn-primary' %>
</div>
<% end %>
</div>
The simplest possible way is just to use String#humanize from ActiveSupport.
Capitalizes the first word, turns underscores into spaces, and (by
default)strips a trailing '_id' if present. Like titleize, this is
meant for creating pretty output.
irb(main):008:0> roles.roles.keys.map(&:humanize)
=> ["User", "Vip", "Admin"]
irb(main):009:0> Profile.new(role: :admin).role.humanize
=> "Admin"
Profile.roles gives us the hash mapping for the Enum.
You can use this to generate the select tag with:
<%= form.select :role, Profile.roles.keys.map{|k| [k.humanize, k] } %>
You can get "vip".humanize to return "VIP" by setting up an inflection:
# config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'VIP'
end
This may require restarting your Rails server before it kicks in.
Using the I18n module
But if you want a more flexible solution that lets you set the mappings yourself (and works with translations) use the I18n module:
# config/locales/en.yml
en:
activerecord:
attributes:
profile:
roles:
user: 'User'
vip: 'Very Important Person'
admin: 'Admin'
# app/helpers/users_helper.rb
module UsersHelper
def translate_role(role)
I18n.t("activerecord.attributes.user.roles.#{ role }", default: role.humanize)
end
def role_options
Profile.roles.keys.map{|k| [translate_role(k), k] }
end
end
You would then display the users role by:
<%= translate_role(#user.role) %>
And you can setup the form input as:
<%= form.select :role, role_options %>
You can use a helper method, For example in the application_helper.rb you can make a method like this:
class ApplicationHelper
def profile_role(profile)
# Perform your logic
# if you are using as_enum gem for example
Profile.roles.select{|symbol_role,integer_rep| integer_rep == profile.role}.keys.first.to_s
# of course there could be much better way to get the string.
end
end
Then in your view, If user should not be able to change it, there is no need to make it as drop-down list:
<div class="row">
<div class="col-md-6 offset-md-3">
<h3>Profile</h3>
<%= form_for(#profile) do |f| %>
<div class="form-group">
<%= f.label :first_name %><br />
<%= f.email_field :first_name, autofocus: true, class: "form-control"%>
</div>
<div class="form-group">
<%= f.label :last_name %><br />
<%= f.password_field :last_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :role %><br />
<%= text_field_tag 'role', profile_role(#profile), disabled: true %>
</div>
<div class="actions form-group">
<%= f.submit "Submit", class: 'btn btn-primary' %>
</div>
<% end %>
You can just show it as regular text, e.g.:
...
<div class="form-group">
<%= f.label :last_name %><br />
<%= f.password_field :last_name, class: "form-control" %>
</div>
<p>
Role:<br />
<%= #profile.role %>
</div>
<div class="actions form-group">
<%= f.submit "Submit", class: 'btn btn-primary' %>
</div>
...

adding a date and time fields in a rails migration

I'm trying to create an event, the event has a date and a time field. When creating the table for protest I didn't add a time field, so I had to run a migration to add a time field. But I am now having issues when creating the form. What is the elegant solution to create a time and date field?
class CreateProtests < ActiveRecord::Migration[5.1]
def change
create_table :protests do |t|
t.string :name
t.text :description
t.string :location
t.datetime :starts_at
t.references :creator, index: true, foreign_key: { to_table: :users }
end
end
end
I then added a field for the time of day:
class AddStartsAtTimeOfDayToProtests < ActiveRecord::Migration[5.1]
def change
add_column :protests, :starts_at_time_of_day, :datetime
end
end
protests/new.html.erb
<div class="col-md-12">
<div class="card">
<div class="card-header"><h2>Create A Protest</h2></div>
<div class="card-block">
<%= form_for(#protest, url: protests_path, html: {multipart: true}) do |f| %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, autofocus: true, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :description %>
<%= f.text_area :description, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :location %>
<%= f.text_field :location, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label "Date:" %>
<%= f.text_field :starts_at, value: "02/16/13", id: "dp2"%>
</div>
<div class="form-group">
<%= f.label "Time:" %>
<%= f.time_select :starts_at, ignore_date: true, class: "form-control"%>
</div>
<div class="form-group">
<%= f.label :image %>
<%= f.file_field :image, as: :file, class: "form-control" %>
</div>
<div class="form-group">
<%= f.submit "Create", class: "btn btn-primary" %>
</div>
<% end %>
</div>
</div>
</div>
The Date and Time are both captured in the single column :starts_at so you don't need another column for the time of day.
Your form should just have:
<div class="form-group">
<%= f.label "Starts At:" %>
<%= f.datetime_select :starts_at, class: "form-control"%>
</div>
Here is a link to the docs:
https://apidock.com/rails/ActionView/Helpers/FormBuilder/datetime_select
add date_field for Date and time_field for the time to have the two fields as follows.
<div class="form-group">
<%= f.label "Date:" %>
<%= f.date_field :starts_at, class: "form-control"%>
</div>
<div class="form-group">
<%= f.label "Time:" %>
<%= f.time_field :starts_at, class: "form-control"%>
</div>

Why won't my has_one relationship work

I have a few models Score ScoreAuthority ScoreColor and ScoreType. Score has_one authority, color, and type. Authority, color, and type each belong_to score. I am having trouble creating the form for Score.
I want users to create this relationship from Scores and everyone seems to handle only the reverse scenario creating from authorities, colors, types forms. What I mean specifically is for example:
Authorities
- foo
- bar
Colors
- red
- blue
I want the use to go into Score, chose to create a new score and then select foo from the drop down list, blue from the colors list, enter a score and submit. Authorities, colors and types will not change much and it would make any sense for the user to select Score from each of those.
When I currently do that I get the following error:
ScoreAuthority(#2248480380) expected, got String(#2155957040)
score/_form.html.erb
<%= form_for #score, :html => { :class => 'form-horizontal' } do |f| %>
<div class="control-group">
<%= f.label :product_id, "Products", :class => 'control-label' %>
<div class="controls">
<%= f.collection_select(:product_id, Product.order(:name), :id, :name) %>
</div>
</div>
<div class="control-group">
<%= f.label :score_authority, "Score Authority", :class => 'control-label' %>
<div class="controls">
<%= f.collection_select :score_authority, ScoreAuthority.order(:name), :id, :name, {}, {:class=>'chosen'} %>
</div>
</div>
<div class="control-group">
<%= f.label :score_type, "Score Types", :class => 'control-label' %>
<div class="controls">
<%#= f.collection_select(:score_type, ScoreType.order(:name), :id, :name) %>
<%= f.collection_select :score_type, ScoreType.order(:name), :id, :name, {}, {:class=>'chosen'} %>
</div>
</div>
<div class="control-group">
<%= f.label :score_color, "Score Types", :class => 'control-label' %>
<div class="controls">
<%= f.collection_select :score_color, ScoreColor.order(:name), :id, :name, {}, {:class=>'chosen'} %>
</div>
</div>
<div class="control-group">
<%= f.label :notation, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :notation, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<%= f.label :value, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :value, :class => 'text_field' %>
</div>
</div>
<div class="form-actions">
<%= f.submit nil, :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
scores_path, :class => 'btn' %>
</div>
<% end %>
score_authorities, score_colors, score_types tables all have score_id as a column. Each of these models also has belongs_to :score.
The score model
has_one :score_authority
has_one :score_type
has_one :score_color
Not sure what I am doing wrong here, any help would be appreciated. Thanks!
you should use :score_authority_id instead of :score_authority in form
<%= f.collection_select :score_authority_id, ScoreAuthority.order(:name), :id, :name, {}, {:class=>'chosen'} %>
<%= f.collection_select :score_color_id, ScoreColor.order(:name), :id, :name, {}, {:class=>'chosen'} %>
<%= f.collection_select :score_color_id, ScoreColor.order(:name), :id, :name, {}, {:class=>'chosen'} %>
see this How do I create the view for the has_one association? for more detail
or you can try
class Score
has_one :score_authority
accepts_nested_attributes_for :score_authority
end
<%= form_for #score ... do |f| %>
...
<%= f.fields_for :score_authority do |b| %>
<%= b.collection_select :id, ScoreAuthority.all, :id, :name %>
...
<% end %>
...
<% end %>

Resources