Rails nested forms link_to_add new entry placement - ruby-on-rails

I have a nested form where the link_to_add appears only by the side of the last entry, meanwhile the previous entries all have only the link_to_remove.
What I need is for it to be possible to:
1- remove the last entry in the form, currently the link_to_remove does not appear for the last entry.
2- add a new entry between two existing ones, example: if there are 2 entries, 1 and 2, I can only append a new entry after 2, I can't add a new entry between 1 and 2.
This is the code for the partial where one of theses nested forms is generated:
<div id="perguntas">
<%= f.fields_for :perguntas do |m| %>
<div class="field plus-field">
<%= m.input :pergunta , :label=> false , :placeholder => "Escreva uma pergunta" %>
</div>
<%= m.link_to_remove "-",:class => "link-add-field" %>
<% end %>
</div>
<div class="adiciona-botao-field ">
<div class="field plus-field">
</div>
<%= f.link_to_add "+", :perguntas, :class => "link-add-field", 'data-target' => '#perguntas' %>
</div>

Related

Issue with form_tag and select_tag in rails

I have a stats controller and view (with no model) that I use to show statistics from another model, company_model. I want to use a select form in the index view of stats and send the selected id from the company in order to hit the show action in stats controller, so i can render the specific stats from that company in particular.
This is how my form in view/stats/index.html.erb looks like:
<%= form_tag("stats", method: :get, enforce_utf8: false) do %>
<div class="form-row col-md-12">
<div class="col-md-4">
<%= select_tag :id, options_for_select(#companies.collect {|c| [ c.name, c.id ] }, params[:id]), include_blank: "Company", class: "form-control margin-bottom-sm" %>
</div>
<%= submit_tag("Show") %>
</div>
</div>
<% end %>
When I hit the buttom, I get stats?id=289&commit=show , and what I want to get instead is stats/289
I really appreciate some help.

Wrong number of arguments (given 4, expected 1..3). - Search bar set up

I'm very new to Rails, so please bear with me. I'm currently trying to add a search widget to my homepage (room booking app).
All I need users to do is to select one of the room attributes (home_type) from the drop down menu and then click the button "search" to see the results. So here is what I'm struggling with:
How should I structure the search part on the homepage. With the approach below I'm getting an error of Argument Error in Pages#home. Wrong number of arguments (given 4, expected 1..3).
<%= form_tag search_path, method: :get do %>
<div class="row">
<div class="col-md-6">
<%= select_tag :room, params[:room], [["Apartment", "Apartment"], ["House", "House"]], id: "room", prompt: "Select...", class: "form-control" %>
</div>
<div class="row">
<div class="col-md-offset-4 col-md-4">
<%= submit_tag "Search", class: "btn btn-normal btn-block" %>
</div>
</div>
<% end %>
I'm trying to adapt parts of an airbnb tutorial to my needs, so not sure if I'm doing it right.
Should be something like this
select_tag :room, [["Apartment", "Apartment"], ["House", "House"]], id: "room", prompt: "Select...", class: "form-control"
Note, that to set a value from params you`ll

Passing parameters on button click in Rails

Right now, I have it so that when users pick an option from collection_select, a div is populated based on the event_id of their choice. Depending on the Event, there will be multiple EventOption shown, each with their own unique ids. (I have EventOption as belongs_to Event, each of which has_many of them.)
This is what I have populating via JS/Ajax when they select an Event:
<div class="center" >
<h2>Available Options for "<%= #event.name %>":</h2>
<% #event.event_options.each do |e| %>
<p><strong>Name:</strong> <%= e.name %> </p>
<p><strong>Description:</strong> <%= e.description %></p>
<p><strong>Price:</strong> <%= e.price %></p>
</div>
<div class = "center row">
<div class = "col-xs-6">
<%= link_to "Check Available Dates", root_path, :class => "button", :id => "available-dates-button" %>
</div>
<div class = "col-xs-6">
<%= link_to "Book This Event Now!", book_now_path(:event => #event.id), :id => "book-now-button", :class => "button" %>
</div>
<hr>
<% end %>
</div>
My issue is that I can pass the event_id when they click the Book This Event Now!, but that's not what I need. I need the specific :id of the :event_option to be passed over, and I don't know how to get it from the iterated .each I have it coming from.
How can I make sure that each iteration of a button that follows my book_now_path has a unique ID that I can use to complete their reservation?
Just add the event_option.id to your book_now_path helper.
book_now_path(:event => #event.id, :event_option_id => e.id)
Then in the controller action it will be in the params:
#event_option_id = params[:event_option_id]
You can add any additional parameters to the Rails routes helpers simply by adding their names and values as keys and values to the helper method's options.

One form element not being rendered when validation fails

I have not been able to figure out if this is a simple_fields_for problem, a cocoon issue, or something else. If I have blundered with a </div> placement, I don't see it.
When the form first displays, it renders an input field for protocol name. The user can click buttons to add form elements of interest. This works fine and looks like this:
Here's how it looks after the user has clicked on each of the "Add an element" buttons:
The user can add 0..many of each of the elements. When they click on 'Save' it all works really well.
If there is a validation error in one of the fields, the form re-renders fine with one exception. Validation errors for the "Imaging Step" elements do not re-display at all. The other elements re-render and are highlighted as expected when validation fails.
Here's a pictorial example. The user fills out part of the form, having forgotten to select a "Sequence" and having forgotten to enter text for "Tip Description":
After clicking 'save' and failing validation, the form re-renders like this:
As you can see, the Imaging Step section has not been redrawn.
If I look at params in the context of the view, everything seems to be there. #protocol.errors looks right to me as well. Models seem OK too.
Here is a pastebin of the form code.
Here is a pastebin of _step_item_fields.html.erb.
Here is a pastebin of _tip_fields.html.erb.
Here is a pastebin of my Gemfile.
UPDATE:
if I build a step_item like this:
<div id="step_items">
<%= f.simple_fields_for :step_items, #protocol.step_items.build do |si| %>
<%= render 'step_item_fields', :f => si %>
<%end%>
</div>
The Imaging Step section is always drawn, but (obviously) does not populate when validation fails. This also confuses the feature of letting the user add/remove 0..many Imaging Steps.
I also tried:
<div id="step_items">
<%= f.simple_fields_for :step_items, #protocol.step_items.build(protocol_params) do |si| %>
<%= render 'step_item_fields', :f => si %>
<%end%>
</div>
... and other variants when protocol_params is available, but ran in to forbidden params issues.
UPDATE2:
I also tried building a hash with params for a step_item. I can use this here:
<div id="step_items">
<%= f.simple_fields_for :step_items, #protocol.step_items.build(some_ok_params) do |si| %>
<%= render 'step_item_fields', :f => si %>
<%end%>
</div>
... but only for a single step_item. Am not sure how to pass a hash of hashes reflecting the 0..many functionality. Also, building a step_item this way populates the form element correctly, but does not include the error highlighting styling. This is about the time I started wondering why the simpler solution was not working.
Since StepItem is a sub-type of the STI class Step, I think the error detection for StepItems was getting missed. My hack fix here is not pretty and admittedly brittle. Please post a better way if you have one.
I changed this in _form.html.erb:
...
<%if params["protocol"] && params["protocol"]["step_items_attributes"].present?%>
<%params["protocol"]["step_items_attributes"].each do |sia|%>
<%v = sia[1]%>
<div id="step_items">
<%= f.simple_fields_for :step_items, #protocol.step_items.build(:orientation_id => v["orientation_id"], :sequence_id => v["sequence_id"], :note => v["note"], :id => v["id"], :protocol_id => v["protocol_id"], :institution_id => v["institution_id"] ) do |si| %>
<%= render 'step_item_fields', :f => si%>
<% end %>
</div><!-- end step_items-->
<%end%>
<%else%>
...
I changed this in _step_item_fields.html.erb:
...
<div class="nested-fields">
<div class= "form-inputs">
<div class="row">
<div class="col-sm-2 text-right">Imaging Step:</div>
<div class="col-sm-2 drop_col ">
<%= f.collection_select :orientation_id, Orientation.where("institution_id=?",current_user.current_institution_id),:id,:name, {:prompt => "Pick an Orientation", }, {class: "form-control #{"dropdown_error" if f.object.orientation_id.blank? && (f.object.sequence_id.present? || f.object.note.present?)}"}%>
</div>
<div class="col-sm-2 drop_col ">
<%= f.collection_select :sequence_id, Sequence.where("institution_id=?",current_user.current_institution_id),:id,:name, {:prompt => "Pick a Sequence"}, {class: "form-control #{ "dropdown_error" if f.object.sequence_id.blank? && (f.object.orientation_id.present? || f.object.note.present?) }"}%>
</div>
</div>
<div class="row">
<div class="col-sm-2"></div>
<%= f.input :note, :wrapper_html =>{:class => 'col-sm-8'}, label: false, placeholder: 'You can put an optional note here.' , :input_html => {:size => 50}%>
<%= f.input :institution_id,:as => :hidden, :input_html => {:value=>current_user.current_institution_id} %>
<%= f.input :id,:as => :hidden %>
<div class="col-sm-2">
<%= link_to_remove_association "Remove Imaging Step", f , :class=>"btn btn-danger btn-xs placemid",title: "Click here to delete this imaging step.", data: {toggle: "tooltip", placement: "auto", animation: true, delay: {show: 700, hide: 100}}%>
</div>
</div>
</div>
<div class="col-sm-12"><hr></div>
</div>
...
And I added this to the correct stylesheet:
.dropdown_error{
color: red;
}
The form now displays StepItem errors correctly as seen here:

Rails - Multiple entries for a single attribute

I am making an app in Rails 4. I use Simple Form.
I have a profile model and a qualifications model.
The associations are:
profile.rb
belongs_to :profile
qualifications.rb
has_many :qualifications
I have a form in my profile views, which includes a part of a form from my qualifications view.
profiles#form
<%= simple_form_for(#profile) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<div class="row">
<div class="intpol2">
Your professional qualifications
</div>
<%= render 'qualifications/form', f: f %>
</div>
Qualifications#form
<%= simple_fields_for :qualification do |f| %>
<div class="form-inputs">
<div class="row">
<div class="col-md-6">
<%= f.input :title, :label => "Your award" %>
</div>
<div class="col-md-6">
</div>
</div>
<div class="row">
<div class="col-md-6">
<%= f.input :level, collection: [ "Bachelor's degree", "Master's degree", "Ph.D", "Post Doctoral award"] %>
</div>
<div class="col-md-6">
<%= f.input :year_earned, :label => "When did you graduate?", collection: (Date.today.year - 50)..(Date.today.year) %>
</div>
</div>
Users may have more than one degree. I want to add a field that is a button which says 'add another qualification' and then a new set of the qualification form fields is available.
I found this post which tries to do something slightly different. I don't want 10 blank sets of the form field (it will make the form look too long).
Creating multiple records for a model in a single view in Rails
Is there another way to achieve this?
You'll be looking for a gem called cocoon; you can also watch this Railscast (Nested forms) which is woefully outdated but still explains the structure very well.
The pattern is very simple, but requires some extra parts:
Have an ajax button which calls the controller
The controller needs to return a form and built fields_for
You'll use JS to append the new fields_for to the original form
The biggest problem is the id of your new fields_for - new implementations of this pattern use child_index: Time.now.to_i
I've written about this here.
Here's a new version:
Ajax
Firstly, you need an "Add Qualification" button, which links to your controller through ajax:
#app/views/profiles/_form.html.erb
<%= simple_form_for(#profile) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<div class="row">
<div class="intpol2">Your professional qualifications</div>
<%= render 'qualifications/form', f: f %>
</div>
</div>
<%= button_to "+", new_profile_path, method: :get %>
<% end %>
Controller
This will go through the new controller method, which we should be able to manage to return the specific response for the ajax request:
#app/controllers/profiles_controller.rb
class ProfilesController < ApplicationController
respond_to :js, :html, only: :new
def new
#profile = Profile.new
#profile.qualifications.build
respond_with #profile #-> will invoke app/views/profiles/new.js.erb
end
end
Response
Once your new.js.erb has fired, we need to build the new HTML form, extract the fields_for and append it to your view:
#app/views/profiles/new.js.erb
var fields_for = $("<%=j render "profiles/form" %>").html(); //-> might need tweaking
$(fields_for).appendTo("#new_profile.form-inputs");
child_index
You should also change your qualifications/form to include the child_index:
#app/views/qualifications/form.html.erb
<%= simple_fields_for :qualifications, child_index: Time.now.to_i do ...
Child index is meant to denote the index of the fields_for elements. In our case (since we're making all new records), it doesn't matter. Using Time.now.to_i ensures a totally unique id each time.
Finally, you need to make sure you're calling:
<%= simple_fields_for :qualifications ... %>
... plural
It seems like that you have to use nested form. You have to try your link tutorial because I will use this too. For another tutorial you can use this as reference nested_forms-rails-4.2.
I hope this help you.

Resources