Dealing with Rails 4 forms and params

In the learning stage of Rails developments one will come across different scenarios of handling parameters from Rails forms that are submitted. Also since Rails 4 introduced Strong Parameters there are some additional things to keep in mind.

Form Params 101

Parameters: { "utf8"=>"✓", "authenticity_token"=>"nqLr/ZukyXt5/sFNU9FGciBbSOSJ2MkE7Roo5WwfVEo=", "profile"=>{ "first_name"=>"Daniel", "last_name"=>"Clark", "socials_attributes"=>{ "0"=>{ "kind"=>"twitter", "username"=>"@6ftdan" } } }, "button"=>"" } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Parameters : { "utf8" = > "✓" , "authenticity_token" = > "nqLr/ZukyXt5/sFNU9FGciBbSOSJ2MkE7Roo5WwfVEo=" , "profile" = > { "first_name" = > "Daniel" , "last_name" = > "Clark" , "socials_attributes" = > { "0" = > { "kind" = > "twitter" , "username" = > "@6ftdan" } } } , "button" = > "" }

Submitted forms return a JSON response with data in it. You can access it from your controller with the params Hash. To access the first name here from params you would have to do params[‘profile’][‘first_name’] and you would get back “Daniel”. With the introduction of Strong Parameters in Rails 4 you are asked to no longer directly use params for security reasons. You will use a middle man method usually named after your model name such as profile_params.

The Strong Params method will look a lot like:

def profile_params params.require(:profile).permit( :first_name, :last_name, :socials_attributes => [:id,:kind,:username,:_destroy]) end 1 2 3 4 5 def profile _ params params . require ( : profile ) . permit ( : first_name , : last_name , : socials_attributes = > [ : id , : kind , : username , : _destroy ] ) end

Consider this strong params method as a read-only parameter access used to help protect you. Also note that this will step into the parameters one level into the params Hash it calls. In this example it returns the “profile” subset of the Hash. So profile_params returns the same results as params[‘profile’] unless additional fields exist that are not within the :permit method. It will drop any items not in :permit.

If you want to modify any of the parameters before your controller saves or updates the Object then you need to perform your work on the params Hash before the save/update methods are invoked. Note: I said to work directly on params for modification or changes. Again; that’s because profile_params is a read-only method and it will be updated accordingly with any changes you commit to params.

Form to params

The form_for method will create the nested params before being submitted. What I mean by that is the Object you give form_for will be the name of the Hash subset that the Strong Params method will have in require(). For example form_for(:profile). will have a params[‘profile’] Hash in the results. In this example case :profile is assigned to an existing Profile Object. Each of the standard input fields will be nested within params[‘profile’]. For example:

<%= form_for(@profile) do |f| %> <%= f.text_field :first_name %> <%= f.submit %> <% end %> 1 2 3 4 5 <%= form_for ( @profile ) do | f | %> <%= f . text_field : first_name %> <%= f . submit %> <% end %>

Will submit a result that you can access first_name through params[‘profile’][‘first_name’] or through the read only strong params method profile_params[‘first_name’]. If you wanted to change the value before it gets saved you need to change it directly on params. For example params[‘profile’][‘first_name’] = params[‘profile’][‘first_name’].reverse will work. But trying the same with profile_params will do nothing in the end.

With form_for you assign it to an object like |f| and each item within it must then be called on f … such as f.text_field. But there are times when you won’t want to do things in this way. When dealing with custom situations in forms there are alternative methods with their counterpart postfixed _tag method.

Dealing with _tag methods

One thing I’ve found when using _tag methods is that they don’t usually follow the same order of method parameters as their non _tag method counterparts. So when using a _tag method it’s best to look up the online method usage reference. My favorite online reference for these methods are apidock.com. Beyond the documentation there are tips and comments on how these methods may be used.

The first thing you should know about _tag methods is that they aren’t nested for Strong Params on their own. In other words you need to nest it yourself. If I replace the f.text_field from the earlier example with text_field_tag I need to use the nested name representation of the parameter I’m setting. For example:

<%= form_for(@profile) do |f| %> <%= text_field_tag 'profile[first_name]', f.object.first_name %> <%= f.submit %> <% end %> 1 2 3 4 5 <%= form_for ( @profile ) do | f | %> <%= text_field _ tag 'profile[first_name]' , f . object . first_name %> <%= f . submit %> <% end %>

This will be true for any _tag method that you want to pass the Strong Parameters requirement. Also notice my use of f.object.first_name. Any time you want to access the values from the Object you’ve piped into the form through |f| you can simply use f.object. f.object is basically the same as accessing @profile directly. There are times when this may come in handy for you.

Another thing to keep in mind with _tag methods is they don’t understand how to get the value for the string ‘profile[first_name]’ to show in the page’s input field. This is why you need to specifically declare the value for it.

Nested Attributes

In the parameter example I gave at the beginning of this you may have noticed:

"socials_attributes"=>{ "0"=>{ "kind"=>"twitter", "username"=>"@6ftdan" } } 1 2 3 4 5 6 7 "socials_attributes" = > { "0" = > { "kind" = > "twitter" , "username" = > "@6ftdan" } }

When you have items of something belonging to another you use nested attributes. In the example above this is a Social Network that belongs to the Profile. So Profile has_many Social Networks and Social Networks belongs_to Profile. Here are the models:

# app/models/profile.rb class Profile < ActiveRecord::Base has_many :socials, as: :sociable, dependent: :destroy accepts_nested_attributes_for :socials end # app/models/social.rb class Social < ActiveRecord::Base enum kind: [ :twitter, :google_plus, :facebook] belongs_to :sociable, polymorphic: true end 1 2 3 4 5 6 7 8 9 10 11 12 # app/models/profile.rb class Profile < ActiveRecord :: Base has_many : socials , as : : sociable , dependent : : destroy accepts_nested_attributes_for : socials end # app/models/social.rb class Social < ActiveRecord :: Base enum kind : [ : twitter , : google_plus , : facebook ] belongs_to : sociable , polymorphic : true end

You’ll notice the word polymorphic. Whenever you create an Object model that can belong to more than one other kind of Object model you will want to use polymorphic. If you didn’t use it you would have to reference profile via a profile_id field in your Social model. With polymorphic it keeps to pieces of information for you: the type and the id. In this case the sociable_type will be “Profile” and the id will reference whatever the owners Profile id is for this “Social Network” Object.

The form that would submit these parameters would look as follows:

<%= form_for(@profile) do |f| %> First Name:<%= f.text_field :first_name %><br /> <%= f.text_field :last_name %><br /> <%= f.fields_for :socials, one_by_kind(@profile.socials, @profile.socials.kinds[:twitter]) do |a| %> <%= a.hidden_field :kind, {value: :twitter} %> Twitter: <%= a.text_field :username %><br /> <% end %> <%= f.submit %> <% end %> 1 2 3 4 5 6 7 8 9 10 11 <%= form_for ( @profile ) do | f | %> First Name : <%= f . text_field : first_name %> < br / > <%= f . text_field : last_name %> < br / > <%= f . fields_for : socials , one_by_kind ( @profile . socials , @profile . socials . kinds [ : twitter ] ) do | a | %> <%= a . hidden_field : kind , { value : : twitter } %> Twitter : <%= a . text_field : username %> < br / > <% end %> <%= f . submit %> <% end %>

The one_by_kind is my own method. I’ve replicated first_or_create with a first or build syntax. You can see my code for this method in a previous article Manual Polymorphic Creation in Rails.

You’ll notice nested attributes are generally given through the fields_for method. fields_for is for all of your belongs_to/has child Objects. You can also replicate the nested fields by using _tag methods. I won’t be covering that at this time though.

Things to Keep in Mind

Be careful about saving user_id from parameters. Think that they may be compromised. Before the save/update commands are committed assign them to the current_user in the controller. Example:

# /app/models/card.rb class Card < ActiveRecord::Base belongs_to :user end # /app/controllers/cards_controller.rb class CardsController < ApplicationController def create @card = Card.new(card_params) @card.user = current_user respond_to do |format| if @card.save ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # /app/models/card.rb class Card < ActiveRecord :: Base belongs_to : user end # /app/controllers/cards_controller.rb class CardsController < ApplicationController def create @card = Card . new ( card_params ) @card . user = current _ user respond _ to do | format | if @card . save . . . _tag methods are done differently then their counterpart methods. For example:

# value is assigned in a options parameter f.hidden_field(:store, {value: 'EatMart'}) # value is assigned by the second parameter given hidden_field_tag('shopping[store]', 'EatMart') 1 2 3 4 5 6 # value is assigned in a options parameter f . hidden_field ( : store , { value : 'EatMart' } ) # value is assigned by the second parameter given hidden_field_tag ( 'shopping[store]' , 'EatMart' ) The Strong Params method is read only. Do any necessary modification on params and please still use the strong_params method to save with. Strong Parameters requires you to declare any field you want to allow in to be saved. Sometimes this will slip your mind. If you’re using form_for or form_tag and the form is closing itself off prematurely (before your inputs) then you can put a tag for a submit button right before the <% end %>. If you don’t want a submit button for the form simply make it invisible.

<%= submit_tag('',style: 'width:0;height:0;display:none;') %> <% end %> 1 2 3 <%= submit_tag ( '' , style : 'width:0;height:0;display:none;' ) %> <% end %>

There are still many things I haven’t covered here. I’ve addressed what I’ve seen as some of the biggest issues when first learning to implement forms in Rails. Things only get more technical when you delve into JavaScript submitted forms via AJAX. It’s not hard, just a bit more to learn. As always I hope what I’ve written has been both helpful to you, and enjoyable to read!

Please comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan!

God Bless!

-Daniel P. Clark