Posting forms with AJAX on checkbox toggle
In a recent Rails 3.1 project, I wanted to have some values updated via AJAX when a checkbox value is checked or unchecked. As I was unable to find anything on the subject online, I figured I would document it here.
Background
I have collections that each have many catalog items. In the collection show view, I want to be able to update a checkbox to indicate whether or not a given catalog item belongs to that collection. In addition, catalog items that are in a collection are highlighted (simply using an “in_collection” CSS class).
HTML markup
<div id="collection_content" collection_id="1">
<div id="catalog_item_id_2">
<input id="collection_catalog_item_id_2" type="checkbox" value="2"
name="collection[catalog_item_ids][]" checked="checked">
<label for="collection_catalog_item_id_2">In collection</label>
</div>
<div>
<!-- another catalog item -->
</div>
</div>
Nothing fancy here, but you’ll notice I included the collection id in the HTML. That is simply because I wanted to include the requisite CoffeeScript in the asset pipeline; therefore, the collection id is made accessible to jQuery by navigating the HTML.
CoffeeScript code
@collection_content_updater = () ->
collection_id = $('#collection_content').attr("collection_id")
$('#collection_content .collection_check_box').each (index, element) ->
checkbox = $(element)
checkbox.click ->
$.post(
"/collections/update_item_for_collection"
catalog_item_id: checkbox.attr("value")
collection_id: collection_id
in_collection: checkbox.is(':checked')
'script'
)
Here, we simply add event listeners to check boxes. These listeners will send the info we need to the collections controller, namely: the catalog item, the collection id, and the flag indicating whether or not the catalog item is in the collection. Note you have to use
checkbox.is(‘:checked’)
to determine whether the checkbox is checked or not, you can’t simply call “checkbox.attr(‘checked’)” as it doesn’t get updated after the HTML is sent to the browser (i.e. (un)checking the checkbox won’t toggle the value).
To actually load this function, we’ll need to add to the CoffeeScript file:
$(->
collection_content_updater()
)
Controller code
def update_item_for_collection
@collection = Collection.find(params[:collection_id])
@catalog_item = CatalogItem.find(params[:catalog_item_id])
@item_is_in_catalog = params[:in_collection] == "true"
if @item_is_in_catalog
@collection.catalog_items << @catalog_item unless @collection.catalog_items.include? @catalog_item
else
@collection.catalog_items.delete(@catalog_item)
end
@collection.save
respond_to do |format|
format.js
end
end
Nothing magical here: just retrieve the necessary model instances, update the collection content, and set variables used in the view.
View code
var $element = $("#catalog_item_#{@catalog_item.id}");
- if @item_is_in_catalog
$element.addClass("in_collection");
- else
$element.removeClass("in_collection");
All we do here is (un)set the "in_collection" HTML class, which will trigger the HTML element to be styled according to the CSS rules.
Would you like to see more Elixir content like this? Sign up to my mailing list so I can gauge how much interest there is in this type of content.