Rails refactor form view - ruby-on-rails

I have rails app with conditions according to params
# _search.html.slim
.search.text-center.my-5
.container
-if params[:city_slug]
= form_tag(city_jobs_path(city_slug: params[:city_slug]), method: :get, class: "form-inline") do
.row
.col-md-8.mb-md-0.no-padding
= search_field_tag :search, params[:search], placeholder: 'Find a job (name, company, position)', class: 'form-control rounded-left no-border-radius bg-light h-100'
.col-md-2.mb-md-0.no-padding
= select_tag(:salary,
options_for_select( #salary_range.collect {|s| [vnd_format(s), s]},
params[:salary] ),
class:"form-select bg-light h-100")
.col-md-2.mb-md-0.no-padding
= submit_tag "Search", class: "h-100 w-100 btn btn-block btn-lg btn-info"
-elsif params[:industry_slug]
= form_tag(industry_jobs_path(industry_slug: params[:industry_slug]), method: :get, class: "form-inline") do
.row
.col-md-8.mb-md-0.no-padding
= search_field_tag :search, params[:search], placeholder: 'Find a job (name, company, position)', class: 'form-control rounded-left no-border-radius bg-light h-100'
.col-md-2.mb-md-0.no-padding
= select_tag(:salary,
options_for_select( #salary_range.collect {|s| [vnd_format(s), s]},
params[:salary] ),
class:"form-select bg-light h-100")
.col-md-2.mb-md-0.no-padding
= submit_tag "Search", class: "h-100 w-100 btn btn-block btn-lg btn-info"
- else
= form_tag(jobs_path, method: :get, class: "form-inline") do
.row
.col-md-4.mb-md-0.no-padding
= search_field_tag :search, params[:search], placeholder: 'Find a job (name, company, position)', class: 'form-control rounded-left no-border-radius bg-light h-100'
.col-md-2.mb-md-0.no-padding
= select_tag(:city,
options_for_select( #city_slug_list,
params[:city] ),
include_blank: 'Select city',
class:"form-select bg-light h-100")
.col-md-2.mb-md-0.no-padding
= select_tag(:industry,
options_for_select( #industry_slug_list,
params[:industry] ),
include_blank: 'Select city',
class:"form-select bg-light h-100")
.col-md-2.mb-md-0.no-padding
= select_tag(:salary,
options_for_select( #salary_range.collect {|s| [vnd_format(s), s]},
params[:salary] ),
include_blank: 'Select salary',
class:"form-select bg-light h-100")
.col-md-2.mb-md-0.no-padding
= submit_tag "Search", class: "h-100 w-100 btn btn-block btn-lg btn-info"
Overall, they look similar. Only the path is different, is there any way to refactor this code

The easiest way to fix DRY here is to extract common erb-code to partial and render it from every template with:
<%= render partial: 'search_fields' %>
Also your can make your partial more universal with passing some options ith there with locals:
<%= render partial: 'search_fields', locals: { industries: false } %>
https://guides.rubyonrails.org/layouts_and_rendering.html#using-partials
BTW
It is not a good practice to access to params object inside views because it is a controller level. This way your code becomes too coupled. It is better to pass required values to instance variables.
Also you can use PageObject pattern to make your code more maintainable.

Related

How to create a nested Form of separate model with many_to_many relationship in Rails?

Need to create one form with entries for 2 different models and establish a join table relationship.
Models:
class Account < ApplicationRecord
has_many :account_authorizations
has_many :authorizations, through: :account_authorizations
accepts_nested_attributes_for :authorizations
end
class Authorization < ApplicationRecord
has_many :account_authorizations
has_many :accounts, through: :account_authorizations
end
class AccountAuthorization < ApplicationRecord
belongs_to :account
belongs_to :authorization
end
Model authorization already has several instances in my DB. There are only 2 attributes for authorization: auth_name and id.
My form initializes with Account. Within this form, I need to have several checkboxes for Authorization entries. I understand fields_for when using a related model but unclear how to implement the check_box method here and capture the values.
Here's what my views page looks like so far:
<% states = [ 'AL', 'AK', 'AS', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DC', 'DE', 'DC', 'FL', 'GA', 'HI', 'ID', 'IL', 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MH', 'MD', 'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM', 'NY', 'NC', 'ND', 'MP', 'OH', 'OK', 'OR', 'PA' 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VT' 'VA', 'WA', 'WV', 'WI', 'WY'] %>
<% ppu = ['Employment', 'Insurance', 'Law Enforement'] %>
<div class="row justify-content-center">
<div class="col-lg-16">
<h3>Create New Account</h3>
</div>
</div>
<div class="row justify-content-center">
<div class="col-lg-16">
<%= form_with(url: accounts_path, model: #paraaccount, local: true) do |f| %>
<%= f.text_field :account_name, class: "form-control", placeholder: "Account Name" %>
<%= f.text_field :account_address_line1, class: "form-control", placeholder: "address line 1" %>
<%= f.text_field :account_address_line2, class: "form-control", placeholder: "address line 2" %>
<%= f.text_field :account_address_city, class: "form-control", placeholder: "account city" %>
<%= f.select :account_address_state, options_for_select(states), { :prompt => true }, class: "form-control", include_blank: true, placeholder: "state" %>
<%= f.text_field :account_address_zipcode, class: "form-control", placeholder: "account zipcode" %>
<%= f.text_field :contact_first_name, class: "form-control", placeholder: "contact first name" %>
<%= f.text_field :contact_last_name, class: "form-control", placeholder: "contact last name" %>
<%= f.text_field :contact_phone, class: "form-control", placeholder: "conact phone" %>
<%= f.text_field :contact_email, class: "form-control", placeholder: "contact email" %>
<%= f.select :account_ppu, options_for_select(ppu), { :prompt => true }, class: "form-control", include_blank: true, placeholder: "ppu" %>
<%= f.text_area :account_notes, class: "form-control", placeholder: "Notes..." %>
<div class="d-flex justify-content-between flex-wrap">
<%= f.fields_for :authorization do |auth| %>
<div class="order-3 p-2 bd-highlight">
# THIS IS WHERE I'M CONFUSED
<%= auth.label #need to get attribute `auth_name` for the label here %>
<%= auth.check_box #need to implement checkboxes here for each entry in `Authorizations` %>
</div>
<% end %>
</div>
<%= f.submit "Create", class: 'btn btn-success' %>
<% end %>
</div>
</div>
I need to capture the values of the checked boxes and use id of Authorization in params to create a join table relationship to Account on creation of the new Account instance.
Hope this makes sense.
you can use collection_check_boxes
<%= form.collection_check_boxes(:authorization_ids, Comment.all, :id, :auth_name) %>
in controller add authorization_ids: [] to permitted params
collection_singular_ids

Bring Drop down label, drop down and submit button in a same line

I am developing a rails application and i am having a drop down submit form as below,
#container
= form_tag({:controller => "r4d", :action=> "result" }, remote: true, method: :get) do
= label_tag(:q, "Trip Type: ")
= select_tag(:q, options_for_select(r4d_options, "r4d_002"), class: "form-control")
= submit_tag("Get Trip Details", :id => "submit", :class => "btn btn-sm btn-default")
The problem here is, the drop down label appears in an line followed by drop down and submit button in an another line. How can i make this appear in same line. I am using bootstrap scss.
Thanks for your help.
Based on the below answer, i have changed the code to
= form_tag({:controller => "r4d", :action => "result", :class => "form-inline"}, remote: true, method: :get) do
.row
.col-sm-12
.col-sm-4
= label_tag(:q, "Trip Type: ")
.col-sm-4
= select_tag(:q, options_for_select(r4d_options), class: "form-control")
.col-sm-4
= submit_tag("Get Trip Details", :id => "submit", :class => "btn btn-sm btn-default")
but there are too much spacing between the form elements and it doesn't looks good.
If you are using haml please try below format for set content in one row
.row
.col-sm-12
.col-sm-6
= label_tag(:q, "Trip Type: ")
= select_tag(:q, options_for_select(r4d_options, "r4d_002"), class: "form-control")
.col-sm-6
= submit_tag("Get Trip Details", :id => "submit", :class => "btn btn-sm btn-default")

Rails form keep sending old values after error

I have a form that for some reason always keep same values after error.
For example, if I enter name that already exists, it will get me an error and at the same time it will keep sending old value even if you change it (so you have to refresh page and lose all changes to actually resolve error).
= provide(:title, t(:create_task))
.center
= form_for :task, url: tasks_path do |f|
= render 'shared/big_flash'
= render 'task_error_messages'
.row.text-center
.col-lg-10.col-lg-offset-1.col-md-12.col-sm-12.col-xs-12
.jumbotron
%h2= t(:create_task)
.row
.col-lg-6.col-md-6.col-sm-6.col-xs-12
= f.label t(:par_task_name)
%i{class: 'material-icons text-muted', rel: 'tooltip', title: t(:tt_task_new_name)} info_outline
= f.text_field 'name', maxlength: 255, class: 'form-control'
= f.label t(:par_category)
= f.collection_select 'category', Category.order(:name), :name, :name, {}, {class: 'form-control'}
= link_to t(:btn_modify_categories), categories_modify_path, class: 'btn btn-primary btn-block', data: { confirm: t(:changes_lost_proceed) }
= f.label t(:par_task_type)
= f.select 'task_type', Task::TASK_TYPES_LIST, {include_blank: true}, {class: 'form-control', id: 'type_of_task'}
= f.label t(:par_score)
= f.number_field 'score', min: '0', step: '1', class: 'form-control'
.col-lg-6.col-md-6.col-sm-6.col-xs-12
= f.label t(:par_asset)
%i{class: 'material-icons text-muted', rel: 'tooltip', title: t(:tt_task_asset)} info_outline
= f.file_field 'category'
.description
= f.label t(:par_description)
%i{class: 'material-icons text-muted', rel: 'tooltip', title: t(:tt_task_description)} info_outline
= f.text_area 'name', class: 'form-control'
= f.check_box 'mathjax'
= f.label t(:par_mathjax)
%i{class: 'material-icons text-muted', rel: 'tooltip', title: t(:tt_task_mathjax)} info_outline
.row
.correct_solutions.col-lg-6.col-md-6.col-sm-6.col-xs-12
= f.label t(:par_correct_solutions)
#generated_correct_solutions
= button_tag t(:btn_add_correct_solution), type: 'button', class: 'btn btn-primary btn-toolbar', id: 'add_correct_solution'
.wrong_solutions.col-lg-6.col-md-6.col-sm-6.col-xs-12
= f.label t(:par_wrong_solutions)
#generated_wrong_solutions
= button_tag t(:btn_add_wrong_solution), type: 'button', class: 'btn btn-primary btn-toolbar', id: 'add_wrong_solution'
#close-ended_task{class: 'hidden'}
= f.check_box 'random', id: 'randomize_task'
= f.label t(:par_random)
%i{class: 'material-icons text-muted', rel: 'tooltip', title: t(:tt_task_random)} info_outline
#randomize{class: 'hidden'}
.row
.col-lg-6.col-md-6.col-sm-6.col-xs-12
= f.label t(:par_number_of_solutions)
%i{class: 'material-icons text-muted', rel: 'tooltip', title: t(:tt_task_no_random_solutions)} info_outline
= f.number_field 'no_random_solutions', min: '1', step: '1', class: 'form-control'
.col-lg-6.col-md-6.col-sm-6.col-xs-12
= f.label t(:par_min_number_of_correct_solutions)
%i{class: 'material-icons text-muted', rel: 'tooltip', title: t(:tt_task_min_no_random_correct_solutions)} info_outline
= f.number_field 'min_no_random_correct_solutions', min: '0', step: '1', class: 'form-control'
.btn-group.btn-group-justified.hidden-xs
.btn-group
= f.submit t(:btn_create_new_task), class: 'btn btn-lg btn-primary btn-toolbar'
.btn-group
= link_to t(:btn_cancel), '#', class: 'btn btn-lg btn-primary btn-toolbar'
.btn-group-vertical.visible-xs
= f.submit t(:btn_create_new_task), class: 'btn btn-lg btn-primary'
= link_to t(:btn_cancel), '#', class: 'btn btn-lg btn-primary'
And my tasks_controller.rb
def new
#task = Task.new
end
def create
#task = Task.new(task_params)
#task.user_id = helpers.current_user.id
if #task.save
flash[:success] = t(:task_created)
redirect_to new_task_path
else
render 'new'
end
end
Any idea what could be the problem?
I don't want to clean up fields after error, I want to keep them, just on next POST, send new values instead.

Search filter applied through "link_to"

So i have a search filter working perfectly in my index view. The code in the controller is as follows
def index
#tutor = Tutor.where(:admin => false)
#tutor_array = []
#tutor_array << #tutor.fees_search(params[:fees_search]) if params[:fees_search].present?
#tutor_array << #tutor.subject_search(params[:subject_search]) if params[:subject_search].present?
#tutor_array << #tutor.lssubject_search(params[:lssubject_search]) if params[:lssubject_search].present?
#tutor_array << #tutor.ussubject_search(params[:ussubject_search]) if params[:ussubject_search].present?
#tutor_array << #tutor.jcsubject_search(params[:jcsubject_search]) if params[:jcsubject_search].present?
#tutor_array.each do |tutor|
ids = #tutor.merge(tutor).map(&:id)
#tutor = Tutor.where(id: ids)
end
#tutor = #tutor.sort_by { |tutor| tutor.rating.rating }.reverse
#tutor = #tutor.paginate(:page => params[:page], :per_page => 2)
end
And in my view the form that passes in the search filters for me is
<form class='form-inline'>
<%= form_tag(tutors_path, method: :get) do %>
<div class='row', id='filter-form'>
<div class='form-group'>
<%= label_tag 'subject_search', 'Primary Subject' %>
<% subject_array = Subject.all.map { |subject| [subject.name] } %>
<%= select_tag 'subject_search', options_for_select(subject_array, :selected => params[:subject_search]), :include_blank => true, class:'form-control' %>
<%= label_tag 'lssubject_search', 'Lower Sec Subject' %>
<% lssubject_array = Lssubject.all.map { |lssubject| [lssubject.name] } %>
<%= select_tag 'lssubject_search', options_for_select(lssubject_array, :selected => params[:lssubject_search]), :include_blank => true, class:'form-control' %>
<%= label_tag 'ussubject_search', 'Upper Sec Subject' %>
<% ussubject_array = Ussubject.all.map { |ussubject| [ussubject.name] } %>
<%= select_tag 'ussubject_search', options_for_select(ussubject_array, :selected => params[:ussubject_search]), :include_blank => true, class:'form-control' %>
</div>
</div>
<div class='row', id='filter-form2'>
<div class='form-group'>
<%= label_tag 'jcsubject_search', 'JC Subject' %>
<% jcsubject_array = Jcsubject.all.map { |jcsubject| [jcsubject.name] } %>
<%= select_tag 'jcsubject_search', options_for_select(jcsubject_array, :selected => params[:jcsubject_search]), :include_blank => true, class:'form-control' %>
<%= label_tag 'fees_search', 'Max Fees' %>
<%= select_tag 'fees_search', options_for_select((10..150).step(10), :selected => params[:fees_search]), :include_blank => true, class:'form-control' %>
<%= submit_tag 'Filter', class: 'btn btn-primary btn-xs' %>
</div>
</div>
<% end %>
<div id='filter-reset'>
<%= link_to 'Reset Filters', tutors_path, class: 'btn btn-primary btn-xs' %>
</div>
</form>
And when a filter is passed in an example or the URL attained is as follows
/tutors?utf8=✓&subject_search=Science&lssubject_search=&ussubject_search=&jcsubject_search=&fees_search=&commit=Filter
What i would like to ask is, how do i pass in the filters through a button?
So if i were to click on "Math" or "English" its equivalent to passing in the filter through the form? I understand that it'll most likely be link_to tutors_path(???) but what should go into the parenthesis to pass the correct filters in?
I tried <%= link_to subs.name, tutors_path(:subject_search => subs.name %> and it seems like it works. To provide some context here's the view and how subs.name comes about.
<% tutor.subjects.each do |subs| %>
<span class='badge'id='tutor-listing-badge'>
<%= link_to subs.name, tutors_path(:subject_search => subs.name) %>
</span>
<% end %>

Form not Rending Correctly in HAML

So I have this form written in haml
= form_tag assign_photographers_path, class: "col-md-12 assign-photographers-form-#{portfolio.id}" do
= hidden_field_tag :portfolio_id, portfolio.id
= hidden_field_tag :user_id_1, "", class: "user_id_1"
= hidden_field_tag :user_id_2, "", class: "user_id_2"
= label_tag :user, "Begin typing to assign the first photographer:"
%br
.col-md-8.col-md-offset-2
= text_field_tag :user, nil, class: "form-control photographer-search", data_id: portfolio.id
.clearfix
%div{class: "photographer-list#{portfolio.id} col-md-6 col-md-offset-3"}
%h3 Chosen photographers
.clearfix
%br
= submit_tag "Assign Photographers", :class => "btn btn-primary"
In the source code it shows
<form accept-charset="UTF-8" action="/photographers/assign" class="col-md-12 assign-photographers-form-19" method="post"></form>
The contents of the form are then printed below the form. I have no idea why it's printing outside of the form tags. What's wrong with the code and how can I fix it?

Resources