#186 Nested Forms from Scratch with StimulusJS

Summary

Using StimulusJS controllers, adding nested forms to a Rails application is easy and unobtrusive. In this episode, we look at an alternative way of creating nested forms without the Cocoon gem.
rails form javascript stimulusjs 16:54

Summary

Terminalgem install rails --pre
rails g scaffold todo_list name
rails g model task todo_list:belongs_to description
yarn add stimulus
models/todo_list.rbclass TodoList < ApplicationRecord
  has_many :tasks, dependent: :destroy
  accepts_nested_attributes_for :tasks, allow_destroy: true, reject_if: proc { |attr| attr['description'].blank? }
end
models/task.rbclass Task < ApplicationRecord
  belongs_to :todo_list
end
todo_lists_controller.rbdef todo_list_params
  params.require(:todo_list).permit(:name, tasks_attributes: [:_destroy, :id, :description])
end
app/javascript/packs/application.jsimport { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"

const application = Application.start()
const context = require.context("../controllers", true, /\.js$/)
application.load(definitionsFromContext(context))
app/javascript/controllers/nested_form_controller.jsimport { Controller } from "stimulus"

export default class extends Controller {
  static targets = ["add_item", "template"]

  add_association(event) {
    event.preventDefault()  
    var content = this.templateTarget.innerHTML.replace(/TEMPLATE_RECORD/g, new Date().valueOf())
    this.add_itemTarget.insertAdjacentHTML('beforebegin', content)
  }

  remove_association(event) {
    event.preventDefault()
    let item = event.target.closest(".nested-fields")
    item.querySelector("input[name*='_destroy']").value = 1
    item.style.display = 'none'
  }
}
todo_lists/_form.html.erb  <h1>Tasks</h1>
  <div data-controller="nested-form">
    <template data-target='nested-form.template'>
      <%= form.fields_for :tasks, Task.new, child_index: 'TEMPLATE_RECORD' do |task| %>
        <%= render 'task_fields', form: task %>
      <% end %>
    </template>

    <%= form.fields_for :tasks do |task| %>
      <%= render 'task_fields', form: task %>
    <% end %>

    <div data-target="nested-form.add_item">
      <%= link_to "Add Task", "#", data: { action: "nested-form#add_association" } %>
    </div>
  </div>
_task_fields.html.erb<div class='nested-fields'>
  <div class='form-group'>
    <%= form.hidden_field :_destroy %>
    <%= form.text_field :description, placeholder: 'Description', class: 'form-control' %>
    <small>
      <%= link_to "Remove", "#", data: { action: "click->nested-form#remove_association" } %>
    </small>
  </div>
</div>


nflorentin said 7 months ago on Nested Forms from Scratch with StimulusJS :

Thanks for this video!
I have one doubt though, the Math.floor(Math.random() * 20 has a high probability to repeat itself. In that case, only one record will be created. Why not generating a more complex number to avoid that ?

kobaltz PRO said 7 months ago on Nested Forms from Scratch with StimulusJS :

I wouldn't say high probability in the real world, but yes it would have a chance. one could use new Date().valueOf() instead, but it too could be duplicated if using a bot.

nflorentin said 7 months ago on Nested Forms from Scratch with StimulusJS :

It produces a number between 0 and 20. Imagine you add 15 tasks to your todolist, probability of having a repeated number is almost 100% ... !

kobaltz PRO said 7 months ago on Nested Forms from Scratch with StimulusJS :

Lol, this is true. My apologies, I should have mentioned taking out 20 to a higher number like Math.floor(Math.random() * 10000000000000000000). Running on fumes this morning.

rcoproc said 7 months ago on Nested Forms from Scratch with StimulusJS :

Congratulations Kobaltz, everything worked properly and great stuff. Thanks for posting on Rails 6 already, a lot of good coming around.

A note (maybe) a little bug I found.
When entering edit mode of some list to do, I tried to do the inclusion of new lines or remove existing ones and it does not work.
No error is shown in console.log.

The add new record and delete events only work when I'm in the include mode of the to-do.

Hugs and good work.

kobaltz PRO said 7 months ago on Nested Forms from Scratch with StimulusJS :

nflorentin mentioned the probability for this occurring. Either take out the 20 number to something much higher or use new Date().valueOf()

Tony Dehnke said 7 months ago on Nested Forms from Scratch with StimulusJS :

I'm a newbie, but wondering if rather than doing the math equation, could you not generate some type of UUID instead? Thanks for making the video, a lot is still over my head, but it's great to watch and see how other people build things.

inopinatus PRO said 7 months ago on Nested Forms from Scratch with StimulusJS :

Would be interested in your take on a Vue.js version e.g. using slots.

kobaltz PRO said 7 months ago on Nested Forms from Scratch with StimulusJS :

I would likely handle it very similar with a Vue controller. The actions on the objects would be slightly modified, but more or less very similar.

Login to Comment