{"id":15612352,"url":"https://github.com/stevepolitodesign/rails-nested-form-example","last_synced_at":"2025-10-13T17:30:54.969Z","repository":{"id":39326365,"uuid":"219999565","full_name":"stevepolitodesign/rails-nested-form-example","owner":"stevepolitodesign","description":"Create a nested form from scratch in Ruby on Rails 6","archived":true,"fork":false,"pushed_at":"2023-03-17T18:33:29.000Z","size":179,"stargazers_count":12,"open_issues_count":1,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-22T01:34:25.842Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://stevepolito.design/blog/create-a-nested-form-in-rails-from-scratch/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stevepolitodesign.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-11-06T13:10:02.000Z","updated_at":"2023-03-17T18:34:32.000Z","dependencies_parsed_at":"2023-02-03T01:31:53.549Z","dependency_job_id":null,"html_url":"https://github.com/stevepolitodesign/rails-nested-form-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevepolitodesign%2Frails-nested-form-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevepolitodesign%2Frails-nested-form-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevepolitodesign%2Frails-nested-form-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevepolitodesign%2Frails-nested-form-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stevepolitodesign","download_url":"https://codeload.github.com/stevepolitodesign/rails-nested-form-example/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":236372273,"owners_count":19138667,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-03T06:41:54.146Z","updated_at":"2025-10-13T17:30:49.653Z","avatar_url":"https://github.com/stevepolitodesign.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Rails Nested Form Example\n\nA guide to adding [fields on the fly](https://guides.rubyonrails.org/form_helpers.html#adding-fields-on-the-fly) in a [nested form](https://guides.rubyonrails.org/form_helpers.html#building-complex-forms) using Rails 6.\n\nInspired by [cocoon](https://github.com/nathanvda/cocoon) and [Ryan Bates](http://railscasts.com/episodes/196-nested-model-form-revised).\n\n## 1. Configuring the Model\n\n```ruby\nclass Person \u003c ApplicationRecord\n    has_many :addresses, inverse_of: :person\n    accepts_nested_attributes_for :addresses, allow_destroy: true, reject_if: :all_blank\nend\n```\n\n## 2. Declare the Permitted Parameters\n\n```ruby\nclass PeopleController \u003c ApplicationController\n    ...\n    private\n\n        def person_params\n            params.require(:person).permit(:first_name, :last_name, addresses_attributes: [:id, :kind, :street, :_destroy])\n        end\nend\n```\n\n## 3. Create a Form Partial\n\n```erb\n\u003c%# app/views/people/_address_fields.html.erb %\u003e\n\u003cdiv class=\"nested-fields\"\u003e\n    \u003c%= f.hidden_field :_destroy %\u003e\n    \u003cdiv\u003e\n        \u003c%= f.label :kind %\u003e\n        \u003c%= f.text_field :kind %\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n        \u003c%= f.label :street %\u003e\n        \u003c%= f.text_field :street %\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n        \u003c%= link_to \"Remove\", '#', class: \"remove_fields\" %\u003e\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\n```erb\n\u003c%# app/views/people/_form.html.erb %\u003e\n\u003c%= form_with model: @person, local: true do |f| %\u003e\n    ...\n    \u003cfieldset\u003e\n        \u003clegend\u003eAddresses:\u003c/legend\u003e\n        \u003c%= f.fields_for :addresses do |addresses_form| %\u003e\n            \u003c%= render \"address_fields\", f: addresses_form %\u003e\n        \u003c% end %\u003e\n        \u003c%= link_to_add_fields \"Add Addresses\", f, :addresses %\u003e\n    \u003c/fieldset\u003e\n\n    \u003c%= f.submit %\u003e\n\u003c% end %\u003e\n```\n\n## 4. Create a Helper Function\n\n```ruby\n# app/helpers/application_helper.rb\nmodule ApplicationHelper\n    def link_to_add_fields(name, f, association)\n        new_object = f.object.send(association).klass.new\n        id = new_object.object_id\n        fields = f.fields_for(association, new_object, child_index: id) do |builder|\n            render(association.to_s.singularize + \"_fields\", f: builder)\n        end\n        link_to(name, '#', class: \"add_fields\", data: {id: id, fields: fields.gsub(\"\\n\", \"\")})\n    end\nend\n```\n\n## 5. Add Javascript\n\n```javascript\n// app/javascript/packs/nested-forms/addFields.js\nclass addFields {\n  constructor() {\n    this.links = document.querySelectorAll(\".add_fields\");\n    this.iterateLinks();\n  }\n\n  iterateLinks() {\n    if (this.links.length === 0) return;\n    this.links.forEach((link) =\u003e {\n      link.addEventListener(\"click\", (e) =\u003e {\n        this.handleClick(link, e);\n      });\n    });\n  }\n\n  handleClick(link, e) {\n    if (!link || !e) return;\n    e.preventDefault();\n    let time = new Date().getTime();\n    let linkId = link.dataset.id;\n    let regexp = linkId ? new RegExp(linkId, \"g\") : null;\n    let newFields = regexp ? link.dataset.fields.replace(regexp, time) : null;\n    newFields ? link.insertAdjacentHTML(\"beforebegin\", newFields) : null;\n  }\n}\n\nwindow.addEventListener(\"turbolinks:load\", () =\u003e new addFields());\n```\n\n```javascript\n// app/javascript/packs/nested-forms/removeFields.js\nclass removeFields {\n  constructor() {\n    this.iterateLinks();\n  }\n\n  iterateLinks() {\n    document.addEventListener(\"click\", (e) =\u003e {\n      if (e.target \u0026\u0026 e.target.className == \"remove_fields\") {\n        this.handleClick(e.target, e);\n      }\n    });\n  }\n\n  handleClick(link, e) {\n    if (!link || !e) return;\n    e.preventDefault();\n    let fieldParent = link.closest(\".nested-fields\");\n    let deleteField = fieldParent\n      ? fieldParent.querySelector('input[type=\"hidden\"]')\n      : null;\n    if (deleteField) {\n      deleteField.value = 1;\n      fieldParent.style.display = \"none\";\n    }\n  }\n}\n\nwindow.addEventListener(\"turbolinks:load\", () =\u003e new removeFields());\n```\n\n```javascript\n// app/javascript/packs/application.js\nrequire(\"./nested-forms/addFields\");\nrequire(\"./nested-forms/removeFields\");\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevepolitodesign%2Frails-nested-form-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevepolitodesign%2Frails-nested-form-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevepolitodesign%2Frails-nested-form-example/lists"}