Rails _destroy field appearing twice in form - ruby-on-rails

I have two models, Preset and Plot, as below:
class Preset < ApplicationRecord
belongs_to :user
has_many :plots, :dependent => :destroy
accepts_nested_attributes_for :plots, allow_destroy: true
end
class Plot < ApplicationRecord
belongs_to :preset
belongs_to :theme, optional: true
end
And a nested form for editing presets:
= form_with(model: #preset, local: true, method: "patch") do |f|
= label_tag(:preset_name, "Preset name:")
= text_field_tag(:preset_name, #preset.name)
%br
= f.fields_for :plots do |builder|
%br
= render 'editplot', f: builder
%br
A partial _editplot that defines the checkbox for destroying the plot, as per railscast 196:
= f.label(:name, "Change plot:")
= f.select(:name, options_for_select([['Existing Plot 1', 'Existing Plot 1'], ['Existing Plot 2', 'Existing Plot 2']]))
= f.label(:_destroy, "Remove plot")
= f.check_box(:_destroy)
I have allowed the _destroy parameter in the presets controller
def preset_params
params.require(:preset).permit(:name, plots_attributes: [:id, :name, :parameter_path, :theme_id, :_destroy])
end
All other aspects of editing the presets work fine, but the checkbox for _destroy does not. The parameters for destroying one of two plots on the edit screen are displayed in console as follows:
Parameters: {"authenticity_token"=>"TOKEN", "preset_name"=>"Preset", "preset"=>{"plots_attributes"=>{"0"=>{"name"=>"Existing Plot 1", "_destroy"=>"1", "id"=>"16"}, "1"=>{"name"=>"Existing Plot 1", "_destroy"=>"0", "id"=>"17"}}}, "commit"=>"Update Preset", "id"=>"25"}
The presence of "_destroy"=>"1" suggests this is working as intended. However, when inspecting the page with Chrome Dev tools it shows there is also a hidden field <input name="preset[plots_attributes][0][_destroy]" type="hidden" value="0"> alongside the checkbox, whose _destroy value of 0 is also passed when the form is submitted. I have a feeling that this element is interfering with the form, but I'm not sure where it's come from or how to get rid of it.
I haven't included it here, but I have some JS code in the same form that adds and removes 'new plot' partials, and these generate their own _destroy fields. I didn't think they would be the cause of the issue, but I can add this code in an edit if necessary.

This is default Rails behavior as explained on the checkbox documentation page.

The issue was not with the checkbox, as pointed out by zwippie, but with my controller. I was trying to update the attributes of the preset and plots manually within the controller (i.e. using lines like #plot.update(name: plot_name, parameter_path: _parameter_path)). Because I was doing this manually, I wasn't actually handling the _destroy parameter and therefore rails wasn't doing anything with it once it had been passed from the form.
To fix this, I used #preset.update(preset_params) instead, where preset_params represents the permitted parameters within the controller. As long as _destroy is permitted, it deletes the object.
def preset_params
params.require(:preset).permit(:name, plots_attributes: [:id, :name, :parameter_path, :theme_id, :_destroy])
end

Related

Test for ActiveAdmin create/edit form - "Add New" record button for associated object

I have an ActiveAdmin form to create a new ActiveRecord object, and the form is capable of also creating records for one of the "main" model's has_many associations, i.e.:
class Car < ApplicationModel
has_many :tires
end
class Tire < ApplicationModel
belongs_to :car
end
# ... in ActiveAdmin "cars.rb" file...
ActiveAdmin.register Car do
...
form do |f|
f.semantic_errors
f.inputs 'Car' do
f.input :color
f.input :doors, as: :number
f.inputs do
f.has_many :tires, allow_destroy: true, new_record: true do |t|
t.input :type
t.input :size
end
end
end
f.actions
end
end
There is also some controller logic not listed here that properly creates, edits, or destroys these records, including Cars and Tires. When the form renders (let's say I'm creating a new Car), there is a button at the bottom that says "Add New Tire". Clicking it renders a sub-form on the page with two fields: "Type" and "Size". I've set up the controllers to save both the Car and its associated Tires when I submit the form. That all works.
I'm struggling with an automated test to ensure that clicking the "Add New Tire" button causes the sub-form to render. I've added a breakpoint after clicking the button and inspected page.body to see if the sub-form exists, but it doesn't seem to be there - I only see what looks like a template of the sub-form in the button's (really an a element) code. This is the part of my test code that fails:
click_link 'New Car'
fill_in('Color', with: 'Blue')
fill_in('Doors', with: 2)
click_link('Add New Tire') # Succeeds
fill_in('Type', with: 'All-Terrain') # Fails, can't find element
I've tried other ways to match on the first sub-form field, like xpath and css selectors, but it just can't find the field, and like I mentioned above, I don't see an instance of the sub-form being rendered if I inspect the page with page.body. I'm stuck - can anyone help me figure out what's going on here?
I got a tip that adding js: true to the test definition could help, and as it turns out, it did:
RSpec.describe Car, js: true do
...
end

Rails 4: How to display fields with error in red?

First question: how to validate a model relation and mark it in form when validation failed.
I have a subject model:
class Subject < ActiveRecord::Base
belongs_to :semester
validates_presence_of :semester
end
In my view (form):
<%= select_tag :semester, options_from_collection_for_select(#semesters,"id","name") %>
The validates_presence_of works fine. But when the validation fails (user forgot to enter semester ). The semester input is not marked in red.
Second question: how to validate an input field.
In my view, I also have a university input field, but model subject has no relationship with university, no field university in subject table. So how to validate it and mark it in red.
Thanks in advance.
If you want to get the fields with error displayed in red "out of the box", you must use a form builder. The code will looks like f.select ... instead of using select_tag.
With the form builder, the fields with errors are created inside a <div class="field_with_errors">...</div>. The css file generated by scaffolding displays these fields in red; if you're not using it, you must add the css rules to your css.
# app/models/subject.rb
class Subject < ActiveRecord::Base
belongs_to :semester
validates :semester, :university, presence: true # new syntax http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates
end
# app/views/subjects/_form.html.erb
<%= form_for #subject do |f| %>
Semestr: <%= f.collection_select :semester_id, Semestr.all, :id, :name, prompt: true %>
University: <%= f.text_field :univercity %>
<% end %>
For more information about building forms in rails (with validations enabled) you could find there http://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html
Those "red errors" that you hope for are probably coming from a form helper gem like formtastic, feel free to check that out.
I have literally no idea what your second question is asking, but if you're looking for a custom validation. Check out the rails docs on them for help.
If you'd like more help, (please) edit your question to be more clear (thanks)

Rails: Drop down with 'Add new' option which creates more form fields

I want to have a drop down on a form. So for example a user is picking an equipment type. But if it is not in the dropdown I want there to be an option for 'Add New' and when this is clicked some more fields pop up that the user must fill in.
So essentially when the form is submitted it creates two new model objects: Equipment and EquipmentType.
Is this possible?
I'm not sure what you exactly need, so I point you to two solutions:
When you clicks "Add new" and you need to fill more then one field.
This is the standart situation for this gems: nested_form and cocoon.
Example of how it looks: http://railscasts.com/episodes/196-nested-model-form-part-1?view=asciicast
When you clicks "Add new" and just want to add another option to
has_many association. In this case you can transform the standart
multiple select into that kind of behavior. Take a look at that
question: https://stackoverflow.com/questions/2867795/best-jquery-multiselect-plugin, there are a couple plugins for this.
Example: http://odyniec.net/projects/selectlist/examples.html
As far as I know, there is still no gem for it. I assume you have two models, Equipment, and EqipmentType, each of them has :name and they are connected via belongs_to/has_many association.
My solution will add an attr_accessor :new_equipment_type to Equipment, which is populated after the form is submitted.
Then, it creates a new equipment type if needed and connects it to the equipment:
# app/models/equipment.rb
class Equipment < ActiveRecord::Base
belongs_to :equipment_type
attr_accessible :name, :equipment_type_id, :new_equipment_type
attr_accessor :new_equipment_type
def new_equipment_type=(val)
if equipment_type_id.blank?
write_attribute(:equipment_type_id, EquipmentType.create(name: val).id)
end
end
end
# app/models/equipment_type.rb
class EquipmentType < ActiveRecord::Base
has_many :equipments
attr_accessible :name
end
The form has a dropdown with existing equipment types and when a blank option is selected, new input is shown for a new equipment type name:
#app/views/equipments/_form.html.haml
= simple_form_for [#equipment] do |f|
= f.input :name
= f.association :equipment_type, include_blank: "Add new"
= f.input :new_equipment_type, input_html: {value: "New equipment type name"}, wrapper_html: {style: "#{if #equipment.equipment_type_id.present? then 'display:none;' else '' end}"}
And finally javascript to show/hide the new equipment type field:
#app/assets/javascripts/equipments.js.coffee:
#showOrHideNewEquipmentTypeField = (drop_down) ->
new_equipment_type = $(drop_down).closest('form').find('div.input[class*=new_equipment_type]')
if $(drop_down).val()
$(new_equipment_type).slideUp()
else
$(new_equipment_type).slideDown()
$ ->
$('[name*=equipment_type_id]').change ->
showOrHideNewCategoryField(#)

How to pass foreign key attributes down through a nested form in Rails 3

I have three models:
class Client < ActiveRecord::Base
has_many :balancesheets
has_many :investment_assets, :through => :balancesheets
class Balancesheet < ActiveRecord::Base
belongs_to :client
has_many :investment_assets, :dependent => :destroy
accepts_nested_attributes_for :investment_assets, :allow_destroy => true
class InvestmentAsset < ActiveRecord::Base
belongs_to :balancesheet
belongs_to :client
I have two questions that have to do with the foreign key client_id. First, when I create a new balancesheet, I use collection_select to select the client from the list. I would rather put the new balancesheet link on the client show page and just pass the client id through to the form so I don't have to choose a client from a list or type it in a field. So first, how do I do that?
Second, the investment_asset model is nested in the balancesheet form. Everything works just fine, except the client_id attribute for the investment_asset is blank. Im not sure why because my associations seem to be okay. So the question is, how do I pass this client_id attribute down through the nested form? I can post models or controllers, just let me know.
UPDATE I have since found the answer to my first question here. However, I am still trying to figure out the second part based on that answer, which is, how do I pass this client_id down through a nested form? To show how it worked passing user id when creating a balancesheet, here is the client show page link:
<%= link_to 'New BalanceSheet',new_balancesheet_path(:id => #client.id) %>
In the balancesheet form I have a hidden field:
<%= f.hidden_field :client_id %>
And in the Balancesheet Controller this is the new action:
#balancesheet = Balancesheet.new(:client_id => params[:id])
This works just fine. So for an investment_asset I am using Ryan Bates nested_form gem which has a little bit different syntax for a link. So inside the balancesheet form the link to add a new investment_asset is:
<%= f.link_to_add "Add asset", :investment_assets %>
I can't figure out how to pass the user id like I did on the balancesheet with:
(:id => #client.id)
I can put the same thing in a new action in the investment_asset controller and add a hidden field, but I'm not sure how to pass it in on the link. Thoughts?
This might be a total hack for all I know, but all I know is very limited. What I ended up doing here was user jQuery to take the id from the balancesheet and force it into a hidden field for investment_asset. jQuery:
// get the value from the balancesheet hidden field that contains the user_id value
var balsheetclientid = jQuery('#hiddenid').attr('value');
// insert it into the value attribute in the hidden field with class name .iaclientid
jQuery('.iaclientid').val(balsheetclientid)
Then my hidden field looks like this:
<%= f.hidden_field :client_id, :class => 'iaclientid', :value => '' %>
It works just fine. Hopefully that's a valid way of doing it.

Validating auto complete input for associations

Ok, so I'm probably missing something obvious here. I'm using rails3-jquery-autocomplete and formtastic to allow the user to select a Category when creating an Item:
class Category < ActiveRecord::Base
has_many :items
end
class Item < ActiveRecord::Base
belongs_to :category
validates :category_id, :presence => true
end
I got this working via:
semantic_form_for #item do |form|
form.inputs do
form.input :category_id, :as => :hidden
form.autocompleted_input :category,
:url => autocomplete_category_path,
:id_element => "#item_category_id"
end
end
That all works fine and dandy; when the user selects the category name, the category_id goes into the hidden field, and the item saves appropriately.
However i run into problems validating when the user doesn't input/select a valid category. What is the appropriate way to validate the following cases:
User types something in to the box that is not valid category. (this needs to fail. It fails now, but the inline errors are actually on the hidden field)
User makes a selection, then changes the value of the text field to an invalid category (resulting in the id in the hidden field becoming stale. This should also fail, but instead it uses the stale selected Id)
Thanks!
It seems like you are looking for jQuery Tokeninput.
You just serve your categories via REST or include the JSON into the initialization
$("#mytokeninputfield").tokenInput("/categories.json",
{
tokenLimit: 1 // allow at most 1 item
});
<input type="text" id="mytokeninputfield" name="item[category_tokens]" data-pre="<%= #user.category_id %>"/>
See railscast #258 on tokenfields and my gem tokenizes as well (although I currently don't use it feel free to report bugs and I'll take care of it. Or just get inspired and do it on your own :) )

Resources