Model with accepted nested attributes stimulus controller to allow adding/deleting of rows to the nested input. e.g. TimeLog might have many TimeLogEntries
Change to accepts nested attributes
has_many :time_log_entries
accepts_nested_attributes_for :time_log_entries, allow_destroy: true
Updated the permitted params on the controller
# Only allow a list of trusted parameters through.
def time_log_params
params.require(:time_log).permit(:date, :notes,
time_log_entries_attributes: [:id, :schedule_id, :employee_id, :end_time, :employee_role_id, :duty_type_id, :hours_worked, :_destroy])
end
Using example by @excid3
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "links", "template" ]
connect() {
this.wrapperClass = this.data.get("wrapperClass") || "nested-fields"
console.log(this.wrapperClass)
}
add_association(event) {
console.log("add association")
event.preventDefault()
var content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime())
this.linksTarget.insertAdjacentHTML('beforebegin', content)
}
remove_association(event) {
event.preventDefault()
let wrapper = event.target.closest("." + this.wrapperClass)
// New records are simply removed from the page
if (wrapper.dataset.newRecord == "true") {
wrapper.remove()
// Existing records are hidden and flagged for deletion
} else {
wrapper.querySelector("input[name*='_destroy']").value = 1
wrapper.style.display = 'none'
}
}
}
Add to app/javascript/controllers/index.js
import NestedFormController from "./nested_form_controller"
application.register("nested-form", NestedFormController)
Need a partial for the nested fields
<%= content_tag :div, class: "nested-fields", data: { new_record: form.object.new_record? } do %>
<div class="grid grid-cols-11">
<div class="col-span-2 border-b border-r border-dashed px-1 text-sm text-gray-700 font-semibold">
SCHEDULE
</div>
<div class="col-span-2 border-b border-r border-dashed px-1">
<%= render Forms::FieldComponent.new(form, :employee_id, label: "Employee", label_class: "sr-only") do |field| %>
<% field.with_input_select(options: employees_for_select(form.object.employee_id), include_blank: true) %>
<% end %>
</div>
<div class="col-span-2 border-b border-r border-dashed px-1">
<%= render Forms::FieldComponent.new(form, :employee_role_id, label: "Role", label_class: "sr-only") do |field| %>
<% field.with_input_select(options: employee_roles_for_select(form.object.employee_role_id), include_blank: true) %>
<% end %>
</div>
<div class="col-span-2 border-b border-r border-dashed px-1">
<%= render Forms::FieldComponent.new(form, :duty_type_id, label: "Duty", label_class: "sr-only") do |field| %>
<% field.with_input_select(options: duty_types_for_select(form.object.duty_type_id), include_blank: true) %>
<% end %>
</div>
<div class="border-b border-r border-dashed px-1">
<%= render Forms::FieldComponent.new(form, :start_time, label: "Start", label_class: "sr-only") do |field| %>
<% field.with_input_time %>
<% end %>
</div>
<div class="border-b border-r border-dashed px-1">
<%= render Forms::FieldComponent.new(form, :end_time, label: "End", label_class: "sr-only") do |field| %>
<% field.with_input_time %>
<% end %>
</div>
<div class="border-b border-r border-dashed px-1">
<%= render Forms::FieldComponent.new(form, :hours_worked, label: "Hours", label_class: "sr-only") do |field| %>
<% field.with_input_text read_only: true %>
<% end %>
</div>
</div>
<% end %>
Then in main form call this as template for new time entries, and for showing all the times.
<div data-controller="nested-form">
<template data-target="nested-form.template">
<%= form.fields_for :time_log_entries, TimeLogEntry.new, child_index: 'NEW_RECORD' do |time_log_entry| %>
<%= render "time_log_entry_fields", form: time_log_entry %>
<% end %>
</template>
<%= render "time_log_entry_fields_header" %>
<%= form.fields_for :time_log_entries do |time_log_entry| %>
<%= render "time_log_entry_fields", form: time_log_entry %>
<% end %>
<div class="m-2" data-target="nested-form.links">
<%= link_to "Add Time Entry", "#", class: "inline-flex items-center rounded-full border border-transparent bg-indigo-100 px-2.5 py-1.5 text-xs font-medium text-indigo-700 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2", data: { action: "click->nested-form#add_association" } %>
</div>
</div>