在hidden_field_tag中传递哈希值

17

我试图通过表单传递一些参数来过滤内容,代码如下:

hidden_field_tag "filters", params[:filters]

但是在下一页中,这些参数会发生改变。例如,如果params[:filters]原本是...

"filters"=>{"name_like_any"=>["apple"]}[1]

...它会被改变成...

"filters"=>"{\"name_like_any\"=>[\"apple\"]}"[2]

可以看到,与[1]相比,[2]中多出了引号和反斜杠。

有什么想法吗?我正在尝试使用searchlogic进行一些过滤操作,但当我在表单中更改对象时,我需要让过滤器保持不变。我希望能够避免将其存储在会话中。

7个回答

20

我的解决方案就是重新创建每个带有键值对的参数:

<% params[:filters].each do |key,value| %>
<%= hidden_field_tag "filters[#{key}]",value %> 
<% end %>

3
非常简单,妙用无穷。谢谢okliv! - Chuck Bergeron

12

您实际上想要/需要使用隐藏字段对哈希表进行“序列化”。

将以下代码添加到您的ApplicationHelper中:

  def flatten_hash(hash = params, ancestor_names = [])
    flat_hash = {}
    hash.each do |k, v|
      names = Array.new(ancestor_names)
      names << k
      if v.is_a?(Hash)
        flat_hash.merge!(flatten_hash(v, names))
      else
        key = flat_hash_key(names)
        key += "[]" if v.is_a?(Array)
        flat_hash[key] = v
      end
    end

    flat_hash
  end

  def flat_hash_key(names)
    names = Array.new(names)
    name = names.shift.to_s.dup 
    names.each do |n|
      name << "[#{n}]"
    end
    name
  end

  def hash_as_hidden_fields(hash = params)
    hidden_fields = []
    flatten_hash(hash).each do |name, value|
      value = [value] if !value.is_a?(Array)
      value.each do |v|
        hidden_fields << hidden_field_tag(name, v.to_s, :id => nil)          
      end
    end

    hidden_fields.join("\n")
  end

然后,在视图中:

<%= hash_as_hidden_fields(:filter => params[:filter]) %>

即使您的过滤器中有多级哈希/数组,这应该可以解决问题。

解决方案取自http://marklunds.com/articles/one/314


搞定了,谢谢。这个解决方案应该被包含在Rails或其他东西中。 - funkymunky
不客气 :). 不,我认为这不是核心功能,但如果它被包含在插件中会很好。 - Vlad Zloteanu
1
太棒了!一定要成为Rails的一部分。顺便提一下,在Rails 5中必须将hidden_fields.join("\n")更改为hidden_fields.join("\n").html_safe,以便HTML不会被转义。 - elsurudo

8

我刚刚编写了一个名为HashToHiddenFields的gem来实现这个功能。

这个gem的核心是this段代码:

def hash_to_hidden_fields(hash)
  query_string = Rack::Utils.build_nested_query(hash)
  pairs        = query_string.split(Rack::Utils::DEFAULT_SEP)

  tags = pairs.map do |pair|
    key, value = pair.split('=', 2).map { |str| Rack::Utils.unescape(str) }
    hidden_field_tag(key, value)
  end

  tags.join("\n").html_safe
end

1

以下是我如何成功地将参数值从视图A通过视图B传递到控制器的方法:

In View A (index):

<%= link_to 'LinkName', {:action => "run_script", :id => object.id} %>

In View B (run_script):

<%= form_tag :action => 'index', :id => @object %>
<%= hidden_field_tag(:param_name, params[:id]) %>

In the controller:

Just reference params[:param_name] to make use of the value.

关键的转换在任何地方都没有记录,我找到的是从视图A中传递 {... :id => object.id} 到视图B,然后作为 <%... :id => @object %> 传递给控制器,通过 hidden_field_tag 构造将其作为 (:param_name, params[:id]) 传递给控制器。

我没有在任何地方看到这个记录,但在浏览了几篇文章后,包括这篇文章(它的语法提供了关键的灵感),解决方案最终成型。我已经看到了有关安全性的隐藏字段的注意事项,但考虑到我的当前设计,我没有找到其他方法来实现这一点。


0
作为对 Vlad 回答的警告,我不得不使用 raw:
<%= raw hash_as_hidden_fields(:filter => params[:filter]) %>

为了让它在Rails 3.1.1中正常工作,需要进行一些调整。基本上,输出的文本被转义了,例如"<"变成了"&lt"。


0

这是因为当你使用hidden_field_tag在HTML中进行转换时,反引号会被添加。之后,当你将其作为字符串而不是哈希接收时。

哈希类型无法存在于HTML中。你只有字符串。所以如果你想传递你的哈希(我不建议),你需要在接收到它时对其进行eval。但这可能会成为你应用程序的一个重大安全问题。


0
假设哈希是字符串、符号、数字和数组,您可以调用 eval 将哈希的 params 字符串从 hidden_fields 表单转换回控制器中的哈希。然后添加引号的反斜杠转义字符就不再是问题了。
hash = eval(params["hash_string"].to_s)

感谢以下文章帮助我找到了解决方案:

如何将字符串对象转换为哈希对象?

请记住,params的内容应该使用.require和.permit进行清理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接