Validating auto complete input for associations - ruby-on-rails

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 :) )

Related

Rails _destroy field appearing twice in form

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

Rendering nested form fields in Rails

In our app, we have, among others, these three models:
class PackContent < ActiveRecord::Base
belongs_to :product
belongs_to :pack
class Product < ActiveRecord::Base
has_many :pack_contents
has_many :packs, through: :pack_contents
class Pack < ActiveRecord::Base
has_many :pack_contents
has_many :products, through: :pack_contents
accepts_nested_attributes_for :pack_contents
A pack has multiple pack_contents, which are basically a product and some specific productvariants. The join table (pack_contents) looks like this:
I am now trying to display these pack_contents in a usable manner, but keep running into a wall.
I have tried:
.pack-product-rows
- #pack.pack_contents.each do |pc|
= f.simple_fields_for pc do |p|
= render :partial => 'pack_product_row', :locals => { :p => p, :pc => pc }
But then, upon saving, I get an error saying:
ActiveRecord::UnknownAttributeError in Admin::PacksController#update
unknown attribute: pack_content
And the form field name attributes are wrong, they don't account for multiple contents as they don't include an index.
When I try this:
.pack-product-rows
- #pack.pack_contents.each do |pc|
= f.simple_fields_for :pack_contents do |p|
= render :partial => 'pack_product_row', :locals => { :p => p, :pc => pc }
It renders every partial twice, which, I suppose, is to be expected.
If i don't loop over the contents and just use simple_fields_for, I get the output I want, but not how I want it. I can render the fields with p.input_field :id and the likes, but I need access to the pack_contents in order to display my data like this: (input fields meant to be hidden)
Not sure how to proceed.
I'm not sure if I've understood what you're asking but if you just want an access to the current item being automatically iterated via simple_fields_for, then you can access it through the form builder as p.object.
I am assuming that you are trying to build the nested form on the views/packs/show.html.erb where in you have the #pack instance variable initialized to a pack from the database. So on this page you will have a - simple_form_for #pack form inside which you want to nest over the pack_contents.
Now the simple_form_fields method of the simple_form gem works just like the fields_for method in rails. Based on that I am giving you the code below. It should work properly but you may have make a few syntactical changes to make it work.
- simple_form_for #pack do |pack_form|
.pack-product-rows
- #pack.pack_contents.each do |pack_content|
= f.simple_fields_for :pack_contents, pack_content do |pack_content_fields|
= render :partial => 'pack_product_row', :locals => { :pack => #pack, :pack_content => pack_content, :pack_content_fields => pack_content_fields}
Now in the partial pack_product_row you will need to use the three locals passed. You must be already using pack and pack_content but whatever fields for pack_contents you want to display should be written as pack_content_fields.field_name. If your naming convention is correct for all the fields then these fields will be either empty if the pack_content is empty or having the value stored in the database.

Can I use accepts_nested_attributes_for with checkboxes in a _form to select potential 'links' from a list

In Rails 3:
I have the following models:
class System
has_many :input_modes # name of the table with the join in it
has_many :imodes, :through => :input_modes, :source => 'mode', :class_name => "Mode"
has_many :output_modes
has_many :omodes, :through => :output_modes, :source => 'mode', :class_name => 'Mode'
end
class InputMode # OutputMode is identical
belongs_to :mode
belongs_to :system
end
class Mode
... fields, i.e. name ...
end
That works nicely and I can assign lists of Modes to imodes and omodes as intended.
What I'd like to do is use accepts_nested_attributes_for or some other such magic in the System model and build a view with a set of checkboxes.
The set of valid Modes for a given System is defined elsewhere. I'm using checkboxes in the _form view to select which of the valid modes is actually set in imodes and omodes . I don't want to create new Modes from this view, just select from a list of pre-defined Modes.
Below is what I'm currently using in my _form view. It generates a list of checkboxes, one for each allowed Mode for the System being edited. If the checkbox is ticked then that Mode is to be included in the imodes list.
<% #allowed_modes.each do |mode| %>
<li>
<%= check_box_tag :imode_ids, mode.id, #system.imodes.include?(modifier), :name => 'imode_ids[]' %>
<%= mode.name %>
</li>
<% end %>
Which passes this into the controller in params:
{ ..., "imode_ids"=>["2", "14"], ... }
In the controller#create I extract and assign the Modes that had their corresponding checkboxes ticked and add them to imodes with the following code:
#system = System.new(params[:system])
# Note the the empty list that makes sure we clear the
# list if none of the checkboxes are ticked
if params.has_key?(:imode_ids)
imodes = Mode.find(params[:imode_ids])
else
imodes = []
end
#system.imodes = imodes
Once again that all works nicely but I'll have to copy that cludgey code into the other methods in the controller and I'd much prefer to use something more magical if possible. I feel like I've passed off the path of nice clean rails code and into the forest of "hacking around" rails; it works but I don't like it. What should I have done?

Rails 3: create input field for each element of a collection (MongoID)

I have two models, Item and Bid
class Item
include Mongoid::Document
field :name, type: String
has_many :bids
end
class Bid
include Mongoid::Document
include Mongoid::Timestamps::Created
field :bid, type: Float
field :bidder, type: String
belongs_to :item
end
In views/prices/index I would like to list all items in a table and for each item put a field next it in which people can enter a number. At the bottom of the form should be a submit button for all records.
How is this best achieved?
There's plenty of code out there explaining how to add, for instance, several questions to one survey (http://railscasts.com/episodes/196-nested-model-form-part-1) but I couldn't find an example that shows how to add one new record for each of an existing element of a collection.
I don't think you need to dynamically add a form for each item if you know you are going to use all of the items there to begin with.
Quick and Dirty:
form_for(#items) do |f|
#items.each do |item|
f.label item.name
f.text_field item.name.to_sym, :value => "1"
end
f.submit "Submit"
end
I haven't tried this and that code isn't tested, but I would assume if you do some work in the controller to build each bid it should be fine. Also amount would be a better variable name for the bid class than using Bid.bid.
The proper way to do this (which would also allow updates) would be to make a nested form and then use fields_for method to actually look at a live bid object: http://archives.ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes

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.

Resources