RJS: Using observe_field on select_tag

妖精的绣舞 提交于 2019-12-05 07:57:17

问题


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"}

回答1:


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"



回答2:


My first thought was it may be some scoping issues. I assume that filter_category_path is one of your route path helpers - the id or the category value (in :with) may not be in scope in the helper method.

When you view the page, can you see the JavaScript that is output by the call to observe_field?

Using Firebug, can you see any Ajax requests being made?

Does anything appear in your development.log?




回答3:


  def observe_category_select
    observe_field(
                  :category,
                  :url        =>  filter_category_path(:id),
                  :with       =>  :category_id,
                  :on         =>  :onchange,
                  :update     =>  :projects
    )
  end

The :onchange is the gotcha - took me a long time to sort it out. EDIT: Looks like you worked that out.

If you want to reset the dropdown and project display state include a function like this in the rendered partial:

<%= link_to_function "close category", :title => 'close category' do |page| 
        page.select("#category").each do |element|
            element.value = "Choose Category"
            page << "window.fireEvent($(\"category\"), 'change');"
        end
        page.replace_html :projects, "<li></li>" 
    end-%>

That will set the select list to the default value and remove all of the list items related to the last displayed category and get the ul ready for the new li's to be displayed from the default. The fireEvent has to be called or the observer won't work if you select the same category that was just hidden.

Since the observer is being fired the controller action has to deal with it:

 def filter
  begin        
    @category = Category.find(params[:category])
    @category_id = @category.id
    @projects = @category.projects.find(:all)
  rescue
    @category = "choose"
  end
  render :layout => false
 end

If no category is found set @category to a string and test in the view partial

<% if @category.eql?('choose') %>
  <li>choose category</li>
<% else %>
 # loop through projects returning them wrapped in li tags
<% end %>

Not pretty but it works.




回答4:


I had the same problem in my actual project. I solved it with the onchange attribute in the select tag:

<%= select_tag @procuct, 
        options_for_select(
            @product.properties.map {|property| [property.name, property.id]}),
        :onchange => "HERE_A_CALL_TO_YOUR_JS_FUNCTION();" %>

UPDATE: I put the Ajax Call in a JS Function, instead you can put everything in the onchange statement, I think you can also put "native Rails" JS methods in there.




回答5:


FWIW - I'm still pretty new to rails and I stumbled on the fact that the new custom action needs to be defined in your routes.rb file (kinda obvious in hindsight, but I had never had to do this before).

If you're using restful routes, then just tack a :collection => {:filter => :get} to the end of your resource route line (e.g. map.resource :categories, :collection => {:filter => :get}).

Hope that helps somebody out.



来源:https://stackoverflow.com/questions/575862/rjs-using-observe-field-on-select-tag

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!