Rails offers all kinds of intuitive solutions for programmers, one of which is form helpers. In this post, I'll be breaking down the steps for creating a nested form - which is basically a form within a form. Formception.
When creating my first Rails project, I found myself searching for guidance on creating this type of form. While there are many sources that show you how to do this with a
has_many association, I struggled to find examples for
belongs_to. So, we will be working with a complex form for a joins table.
We're building a mood diary. We have two models to work with regarding our nested form: Entry and Emotion.
Entry is our joins table and so it's association is
belongs_to :emotion, while Emotion
When imagining the functionality of our form, we can conclude that we want to have the ability to create our Entry and either assign it an existing emotion or create a new one.
First, we want to use
accepts_nested_attributes_for class method in our Entry model. This method defines an attributes writer for the specified association, in this case, Emotion.
Going along with the Rails penchant for convention over configuration, a good rule of thumb to remember when setting up nested forms is:
If the association 'belongs to' a singular model (e.g., emotion) we will maintain the singular form of reference. If the association 'has many' plural models we will maintain the plural form of reference throughout our code.
Now we will need to modify our declared permitted parameters to accommodate the nested attributes. Following our example, we will add this to our EntriesController. In the code below I have added a key for association attributes, where the association is the relationship you want to 'nest' into your form. Put simply, it's the one you added to
accepts_nested_attributes_for. This key holds the specified association's attributes that we want passed in.
Note: In this example,
:emotion_id is included outside of
emotion_attributes: - As shown in the Rails guide we can also use:
def entry_params params.require(:entry).permit(:content, emotion_attributes: [ :id, :name ]) end
Finally, we are ready to create our form!
The magic happens within the
fields_for tag which, when associated with
accepts_nested_attributes, renders a block once for every element of the association. We then use the
build_association method which instantiates the object from the passed attributes and sets a link through the foreign key.
Note: Pay close attention to
build_association method. Not to be confused with
collection.build, which instantiates an object from the passed attributes and creates a foreign key. The latter is a method made accessible when declaring a
Our final product (plus a bit of CSS) looks like this, with the option to select or create an emotion: