2

只要用户更改下拉菜单中的值并点击“开始”按钮,我的应用程序中的下拉菜单(由 select_tag 构建)应该调用过滤器类别操作。

现在,我想摆脱“开始”按钮并让观察者(observe_field?)在用户更改下拉菜单中的值后立即调用过滤器类别操作。

下面你会看到我写的代码。它可以使用“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 -%>

辅助方法

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

HelperMethod 的 Javascript 输出

<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_category POST   /categories/:id/filter                   {:controller=>"categories", :action=>"filter"}
   formatted_filter_category POST   /categories/:id/filter.:format           {:controller=>"categories", :action=>"filter"}
4

5 回答 5

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"
于 2009-02-22T23:31:26.230 回答
1

我的第一个想法是这可能是一些范围问题。我假设 filter_category_path 是您的路由路径助手之一 - id 或类别值(在:with 中)可能不在助手方法的范围内。

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

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

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

于 2009-02-22T21:45:43.130 回答
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 %>

不漂亮,但它有效。

于 2009-03-10T22:27:01.090 回答
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 语句中,我认为您也可以将“本机 Rails”JS 方法放在那里。

于 2009-02-23T09:19:22.230 回答
0

FWIW - 我对 Rails 还是很陌生,我偶然发现新的自定义操作需要在您的 routes.rb 文件中定义(事后看来有点明显,但我以前从未这样做过)。

如果您使用的是宁静的路线,那么只需将 :collection => {:filter => :get} 添加到资源路线行的末尾(例如 map.resource :categories, :collection => {:filter => :得到})。

希望能帮助别人。

于 2010-01-06T22:32:34.657 回答