RJS: Using observe_field on select_tag

非 Y 不嫁゛ 提交于 2019-12-03 20:28:55

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"

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?

  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.

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.

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.

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