Introduce
Instead of just getting used to creating and updating a record of an object, we can now create and update child objects that are linked to each other. Nested Attributes allow you to save the properties of the object’s record through its parent object. By default, Nested Attributes is turned off so you need to add the model class #accepts_nested_attributes_for. For example:
1 2 3 4 5 6 7 | class Book < ActiveRecord::Base has_one :author has_many :pages accepts_nested_attributes_for :author, :pages end |
Practice:
To get acquainted quickly with Nested Attributes, we try to create a simple small app
1 2 | rails new demo_app -d mysql |
Create 2 models
1 2 3 | rails g scaffold user name:string email:string rails g model address user:references street:string |
run rails db:migrate
On the User model:
1 2 3 4 5 6 | class User < ApplicationRecord has_many :address accepts_nested_attributes_for :address end |
In UsersController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def new @user = User.new // tạo mới một instance quan hệ liên kết, ta có thể tạo 1 hay nhiều instance cùng một lúc @user.address.build @user.address.build end ... private def user_params // cập nhật strong parameters cho phép tạo User và Address cùng lúc params.require(:user).permit(:name, :email, address_attributes: [:id, :street, :_destroy]) end |
In views / users / _form.html.erb we use fields_for used in Nested Forms
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <%= form_with(model: user, local: true) do |form| %> <div class="field"> <%= form.label :name %> <%= form.text_field :name %> </div> <div class="field"> <%= form.label :email %> <%= form.text_field :email %> </div> <div class="field"> <%= form.fields_for :address do |ff| %> <%= ff.label :street %> <%= ff.text_field :street %> <% end %> </div> <div class="actions"> <%= form.submit %> </div> <% end %> |
So far, we have built a nested forms that can create a user object and link its address at the same time.
A little more advanced
The above is a basic guide to getting to know Nested Attributes and Nested forms. The downside of the above form is only a static form, we can not know in advance how many addresses the user wants to add, so now let’s try to create a dynamic form that allows the user to add an address with just one click.
Edit the form a little bit, add more classes and add js, css.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <div class="field address-container"> <%= form.label :street %> <%= form.fields_for :address do |ff| %> <%= ff.text_field :street, class: "street-field" %> <% end %> </div> <div class="field"> <button type="button" class="js-btn-add">Add address</button> </div> // thêm js <script type="text/javascript"> $(".js-btn-add").on("click", function() { addAddressField(); }) function addAddressField() { var length = $(".street-field").length; var div = document.createElement("div"); var input = document.createElement("input"); input.type = "text"; input.name = "user[address_attributes]["+ length +"][street]" input.className = "street-field" input.id = "user_address_attributes_" + length + "_street" div.appendChild(input) $(".address-container").append(div); } </script> // thêm css <style type="text/css"> .street-field { margin-top: 5px; margin-bottom: 5px; } </style> |
After the above step you will have a form like this. Now it has become a Dynamic form that allows you to create more addresses.
The next only need to fill in information and click submit to see the results.
Source:
Above are the basic steps to quickly familiarize yourself with Nested Attributes. In addition to more carefully can refer to below.
https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
http://jyrkis-blogs.blogspot.com/2014/06/adding-fields-on-fly-with-ruby-on-rails.html
https://www.tutorielsenfolie.com/en/tutorials-7-Dynamic-form-in-JavaScript.html