RJS:在select_tag上使用observe_field

时间:2009-02-22 21:38:23

标签: ruby-on-rails rjs

我的应用程序中的下拉菜单(由select_tag构建)应在用户更改下拉菜单中的值时立即调用filter-category-action并点击“Go”按钮。

现在我想摆脱'Go'按钮并让观察者(observe_field?)在用户更改下拉菜单中的值时立即调用filter-category-action。

下面你看到我写的代码。它使用'Go'按钮工作,但只是更改下拉菜单中的值不起作用。我的observe_category_select-helper出了什么问题?

使用下拉菜单和项目列表查看部分

    <!-- 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-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>

类别控制器

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

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

5 个答案:

答案 0 :(得分:6)

您的过滤器控制器操作需要响应Javascript,而不仅仅是响应正常的HTTP请求。

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

或者,如果您希望该操作在任一上下文中响应,请将两者放在块中:

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

这要求您拥有一个类似于:

的视图文件filter.rjs
page.replace_html :id_of_element_to_replace_html, :partial => "name_of_partial"

答案 1 :(得分:1)

我首先想到的可能是一些范围问题。我假设filter_category_path是你的路径路径助手之一 - id或类别值(in:with)可能不在helper方法的范围内。

当您查看该页面时,是否可以看到通过调用 observe_field 输出的JavaScript?

使用Firebug,您能看到正在发出的任何Ajax请求吗?

您的development.log中是否有任何内容?

答案 2 :(得分:1)

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

:onchange是陷阱 - 花了我很长时间来解决它。编辑:看起来你已经解决了这个问题。

如果要重置下拉列表,项目显示状态在渲染的部分中包含这样的函数:

<%= 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-%>

这会将选择列表设置为默认值,并删除与上次显示的类别相关的所有列表项,并准备好ul以从默认值中显示新的li。如果选择刚刚隐藏的相同类别,则必须调用fireEvent或观察者不起作用。

由于观察者被解雇,控制器动作必须处理它:

 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

如果未找到任何类别,请将@category设置为字符串并在视图部分

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

不漂亮,但有效。

答案 3 :(得分:0)

我在实际项目中遇到了同样的问题。我用select标签中的onchange属性解决了它:

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

更新:我把Ajax调用放在JS函数中,而不是你可以把所有东西都放在onchange语句中,我想你也可以把“native Rails”JS方法放在那里。

答案 4 :(得分:0)

FWIW - 我对rails很陌生,我偶然发现需要在你的routes.rb文件中定义新的自定义动作(后见之明有点明显,但我以前从未这样做过)

如果你正在使用宁静的路线,那么只需添加:collection =&gt; {:filter =&gt; :get}到资源路线的末尾(例如map.resource:categories,:collection =&gt; {:filter =&gt;:get})。

希望能帮助别人。