Search code examples
ruby-on-railsrjs

RJS: Using observe_field on select_tag


The dropdown-menu (built by the select_tag) in my application should call the filter-category-action as soon as the user changes the value in the dropdown-menu AND hits the 'Go' button.

Now I'd like to get rid of the 'Go' button and have an observer (observe_field?) call the filter-category-action as soon as the user changes the value in the dropdown-menu.

Below you see the code I've written. It works using the 'Go'-Button but doesn't work by just changing the value in the dropdown-menu. What's wrong with my observe_category_select-helper?

View-partial with dropdown-menu and project list

    <!-- drop down menu -->
    <% form_tag(filter_category_path(:id), :method => :post, :class => 'categories') do %>
       <label>Categories</label>
       <%= select_tag(:category, options_for_select(Category.all.map {|category| [category.name, category.id]}, @category_id)) %>
       <!-- i would like to get rid of this button -->
       <%= submit_tag "Go" %>
     <% end %>

   <!-- list of projects related to categories chosen in drop down menu -->
   <ul class="projects">
     <% @projects.each do |_project| %>
       <li>
         <%= link_to(_project.name, _project) %>
       </li>
     <% end %>
   </ul>

   <%= observe_category_select -%>

HelperMethod

  def observe_category_select
    observe_field(
                  :category,
                  :url        =>  filter_category_path(:id),
                  :with       =>  'category',
                  :on         =>  'change'
    )
  end

Javascript-Output of the HelperMethod

<script type="text/javascript">
//<![CDATA[
   new Form.Element.EventObserver('category', function(element, value) {
     new Ajax.Request('/categories/id/filter', {asynchronous:true, evalScripts:true, parameters:'category=' + encodeURIComponent(value) + '&authenticity_token=' + encodeURIComponent('edc8b20b701f72285068290779f7ed17cfc1cf8c')})
   }, 'change')
//]]>
</script>

Categories controller

class CategoriesController < ApplicationController
  def show
    @category = Category.find(params[:id])
    @category_id = @category.id
    @projects = @category.projects.find(:all)

    respond_to do |format|
      format.html # index.html.erb
    end
  end

  def index
    @projects = Category.find(params[:id]).projects.find(:all)

    respond_to do |format|
      format.html # index.html.erb
    end
  end

  def filter
    @category = Category.find(params[:category])
    @category_id = @category.id
    @projects = @category.projects.find(:all)

    respond_to do |format|
      format.html # index.html.erb
    end    
  end

end

Output of 'rake routes | grep filter'

             filter_category POST   /categories/:id/filter                   {:controller=>"categories", :action=>"filter"}
   formatted_filter_category POST   /categories/:id/filter.:format           {:controller=>"categories", :action=>"filter"}

Solution

  • Your filter controller action needs to respond to Javascript instead of just to a normal HTTP request.

    def filter
      @category = Category.find(params[:category])
      @category_id = @category.id
      @projects = @category.projects.find(:all)
    
      respond_to do |format|
        format.js # filter.rjs
      end    
    end
    

    Or, if you want that action to respond in either context, put both in the block:

    respond_to do |format|
      format.html # filter.html.erb
      format.js # filter.rjs
    end    
    

    This requires you to have a view file filter.rjs that will look something like:

    page.replace_html :id_of_element_to_replace_html, :partial => "name_of_partial"