I'm having a hard time figuring out how to combine Selectize.js with a belongs_to association in rails. I want to do something like this photo:
I've attempted using accepts_nested_attributes
, but that doesn't seem to work with a belongs_to
relationship.
I tried doing an auto-complete association like this railscast episode.
What I'd really like to do is use a Selectize style collection select to create the "Speaker" association if it's already in the database, but add a new one if it doesn't yet exist. Selectize enables me to add a new one, but I'm having trouble passing that through the form to create the new record in the associated model.
Here are my models:
class Quote < ApplicationRecord
belongs_to :speaker, class_name: "Artist"
belongs_to :topic, class_name: "Artist"
end
Quote.rb
class Artist < ApplicationRecord
has_many :spoken_quotes, class_name: "Quote", foreign_key: :speaker_id
has_many :topic_quotes, class_name: "Quote", foreign_key: :topic_id
end
Artist.rb
And my form:
<%= f.label :speaker, 'Who said it?' %>
<%= f.collection_select :speaker_id, Artist.order(:name), :id, :name, {prompt: 'Select an artist'}, {class: 'form-control select-artist'} %>
_form.html.erb
Controllers:
quotes_controller.rb
artists_controller.rb
How can I create a new Artist (as "Speaker") through the Quote.new form through the Selective-style collection select? The Selectize behavior is the user experience I'm looking for, I just can't figure out how to create the new Artist through the Quote form.
If you must use the selectize user experience, you might need to create the artist/speaker via ajax using javascript. With the selectize-rails
gem, jquery-rails
and a bit of javascript code, you can either:
create the artist/speaker via ajax and assign the value and id to the quotes form input - see demo or
pop up a modal with the artist form, submit the form via ajax and assign the value and id to the quotes form input
I've attempted to scaffold this simple rails app with a basic structure of what you're trying to achieve to show you an example of option 1. I've included setup instructions and a demo in the readme.
Major changes required are:
Add the selectize-rails
and jquery-rails
gems to your Gemfile
and run bundle install.
Add the selectize class to the collection_select input tag
# /views/quotes/_form.html.erb
<%= f.collection_select :artist_id, Artist.order(:name), :id, :name, {prompt: 'Select an artist'}, {class: 'selectize'} %>
Create /assets/javascript/quotes.js
and make the following changes.
# /assets/javascript/quotes.js
$(document).on("turbolinks:load", function(){
$(".selectize").selectize({
create: function(input, callback) {
$.post('/artists.json', { artist: { name: input } })
.done(function(response){
console.log(response)
callback({value: response.id, text: response.name });
})
}
});
})
artists_controller
Modify the artists_controller#create
action method to be able to render json response.
# POST /artists
# POST /artists.json
def create
@artist = Artist.new(artist_params)
respond_to do |format|
if @artist.save
format.html { redirect_to @artist, notice: 'Artist was successfully created.' }
format.json { render :show, status: :created, location: @artist }
else
format.html { render :new }
format.json { render json: @artist.errors, status: :unprocessable_entity }
end
end
end
You can also watch this GoRails video to see how to achieve option 2.
If you're providing create-on-missing functionality, and the only needed value is the one in the selector input, consider skipping the id
entirely:
class Quote < ApplicationRecord
def speaker_name
speaker&.name
end
def speaker_name=(name)
self.speaker = name.presence && Artist.find_or_initialize_by(name: name)
end
And over in the view:
<%= f.label :speaker, 'Who said it?' %>
<%= f.collection_select :speaker_name, Artist.order(:name), :name, :name, {prompt: 'Select an artist'}, {class: 'form-control select-artist'} %>
(with a matching change in QuotesController#quote_params
)
Then when you Selectize-ify the input, allowing new values to be typed, it should Just Work. Notably, this avoids eagerly creating the artist record via AJAX, which can create orphans if someone types an entry but then never submits the form.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With