Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mnishiguchi/38c46c8ddba5c92cfb1169521f9397c6 to your computer and use it in GitHub Desktop.
Save mnishiguchi/38c46c8ddba5c92cfb1169521f9397c6 to your computer and use it in GitHub Desktop.

Rails - Selectize field with modal add form.

UI flow

- User types a word and hit the `Add ...` button. -> Modal opens with an add form.
- User fill the new-category form and submit.     -> The form data is persisted to the database
- If user closes the modal without submitting the form, the form will be cleared and the modal will close.

Add dependencies

Gemfile

gem 'selectize-rails'

app/assets/javascripts/application.js

//= require jquery
//= require jquery_ujs
//= require tether
//= require turbolinks
//= require bootstrap
//= require selectize
//= require_tree .

app/assets/stylesheets/application.scss

/*
 *= require selectize
 *= require selectize.default
 *= require_tree .
 *= require_self
 */

View

app/views/posts/new.html.slim

= form_for(post) do |f|
  - if post.errors.any?
    #error_explanation
      h2
        = pluralize(post.errors.count, "error")
        | prohibited this post from being saved:
      ul
        - post.errors.full_messages.each do |message|
          li= message
  .form-group
    = f.label :title
    = f.text_field :title, class: "form-control"
  .form-group
    = f.label :body
    = f.text_area :body, class: "form-control"

  -# Seleclize is applied to this field
  .form-group
    = f.label :category_ids, "Categories"
    = f.select :category_ids, Category.all.pluck(:name, :id), {}, { multiple: true, class: "selectize" }

  .form-group
    = f.submit class: "btn btn-primary"

-# The add-form modal
.modal.fade.new_category_modal [aria-labelledby="categoryModalLabel" role="dialog" tabindex="-1"]
  .modal-dialog.modal-sm [role="document"]
    .modal-content
      .modal-header
        button.close [aria-label="Close" data-dismiss="modal" type="button"]
          span[aria-hidden="true"]  
            | ×
        h4#categoryModalLabel.modal-title
          | Add Category
      .modal-body
        = form_for Category.new do |f|
          .form-group
            = f.label :name
            = f.text_field :name, class: "form-control"
          .form-group
            = f.label :description
            = f.text_area :description, class: "form-control"
          .form-group
            = f.submit class: "btn btn-primary"

JS

app/assets/javascripts/posts.js

$(document).on("turbolinks:load", function() {
  // NOTE: When the selectize's `add ...` button is clicked, it will wait until
  // we call the callback function whether or not data is updated.
  // So we will store the ref to the callback function so that we can call it when we need to.
  var selectizeCallback = null

  // Set up the selectize on the target element.
  $(".selectize").selectize({
    plugins: ['remove_button'],

    // When the `add ...` button is clicked
    create: function(input, callback) {
      // Store the ref to the callback so that we can notify the selectize when we are done.
      selectizeCallback = callback

      openModal()

      // Fill the field with the value provided by the selectize.
      $("#category_name").val(input)
    }
  })

  // Handle the modal being closed.
  $(".new_category_modal").on("hide.bs.modal", function(e) {
    notifySelectize()
    resetForm()
  })

  // Handle the add-category form's submission.
  $("#new_category").on("submit", function(e) {
    e.preventDefault()
    $.ajax({
      method : "POST",
      url    : $(this).attr("action"),  // The form tag's `action` attribute
      data   : $(this).serialize(),     // The data from the selectize
      success: function(response) {
        notifySelectize({ value: response.id, text: response.name })
        closeModal()
      }
    })
  })

  // ---
  // PRIVATE FUNCTIONS
  // ---

  // Notify the selectize that we are done. Because the form was not submitted, we have no data to pass in.
  function notifySelectize(data) {
    if (selectizeCallback != null) {
      selectizeCallback(data)
      selecitzeCallback = null
    }
  }

  function resetForm() {
    $("#new_category").trigger("reset")             // Reset the form
    $.rails.enableFormElements($("#new_category"))  // Re-enables disabled form elements
  }

  function openModal(input) {
    $(".new_category_modal").modal()
  }

  function closeModal() {
    $(".new_category_modal").modal('toggle')
  }
})

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment