Passing hash as values in hidden_field_tag
Asked Answered
I

7

18

I am trying to pass some filters in my params through a form like so:

hidden_field_tag "filters", params[:filters]

For some reason the params get changed in the next page. For example, if params[:filters] used to be...

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

...it gets changed to...

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

note the extra quotations and backslashes in [2] when compared to [1].

Any ideas? I'm attempting to use this with searchlogic for some filtering, but I need it to persist when I change change objects in forms. I would prefer not to have to store it in session.

Importunate answered 24/3, 2010 at 7:7 Comment(0)
J
12

You actually want/need to 'serialize' a hash using hidden fields.

Add this to your 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

Then, in view:

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

This should do the trick, even if you have a multilevel hash/array in your filters.

Solution taken http://marklunds.com/articles/one/314

Jetliner answered 24/3, 2010 at 11:1 Comment(3)
That did it thanks. This solution should be included in Rails or something.Importunate
You're welcome :). No, i believe this is not a core functionality, but it would be nice if it wold be included in a plugin.Jetliner
Awesome! Should definitely be a part of Rails. Btw has to change hidden_fields.join("\n") to hidden_fields.join("\n").html_safe in Rails 5, so that the HTML doesn't get escapedGorrian
S
21

My solution was just to re-create each of param with key-value pair:

<% params[:filters].each do |key,value| %>
<%= hidden_field_tag "filters[#{key}]",value %> 
<% end %>
Spiraea answered 10/11, 2011 at 13:46 Comment(0)
J
12

You actually want/need to 'serialize' a hash using hidden fields.

Add this to your 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

Then, in view:

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

This should do the trick, even if you have a multilevel hash/array in your filters.

Solution taken http://marklunds.com/articles/one/314

Jetliner answered 24/3, 2010 at 11:1 Comment(3)
That did it thanks. This solution should be included in Rails or something.Importunate
You're welcome :). No, i believe this is not a core functionality, but it would be nice if it wold be included in a plugin.Jetliner
Awesome! Should definitely be a part of Rails. Btw has to change hidden_fields.join("\n") to hidden_fields.join("\n").html_safe in Rails 5, so that the HTML doesn't get escapedGorrian
A
8

I just wrote a gem to do this called HashToHiddenFields.

The core of the gem is this code:

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
Admixture answered 28/2, 2012 at 19:18 Comment(1)
Thanks for your answer, your gem and you work on this problem + sharing it with the world. I have question: is it safe / dangerous to call html_safe on user input?Jessee
N
1

Here's how I managed to pass a parameter value through my view - that is, from View A through View B and on to the controller:

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.

The key transition that wasn't documented anywhere I could find is where {... :id => object.id} from View A is passed on to View B as <%... :id => @object %>, which View B then passes on to the controller as (:param_name, params[:id]) through the hidden_field_tag construct.

I didn't see this documented anywhere but after perusing several posts across several sites including this post (whose syntax provided the key inspiration), the solution finally gelled. I've seen the caveats on hidden fields pertaining to security but have found no other way to do this given my current design, such as it is.

Nourishing answered 6/3, 2012 at 23:38 Comment(0)
G
0

it's because when you convert in HTML with your hidden_field_tag, the backquote is add. After when you received it like a string not a Hash.

The Hash type can't exist in HTML. You have only string. So if you want pass your hash (not recommend by me), you need eval it when you received it. But can be a big security issue on your application.

Galipot answered 24/3, 2010 at 8:0 Comment(0)
L
0

As a caveat to Vlad's answer, I had to use raw:

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

to get it to work in Rails 3.1.1. Essentially, the text being output was being escaped, eg., "<" becoming "&lt".

Laager answered 5/11, 2011 at 21:41 Comment(0)
D
0

Assuming the hash is strings, symbols, numbers, and arrays, you can call eval to convert the params string of the hash from the hidden_fields form back into a hash in the controller. Then the backslash escape characters for the quotes added are no longer an issue:

hash = eval(params["hash_string"].to_s)

Credit to the following article for helping identify this simple solution for my case:

How do I convert a String object into a Hash object?

Keep in mind the contents of the params should be cleaned with .require and .permit.

Diode answered 11/12, 2019 at 4:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.