在Rails中通过点击按钮使用jQuery创建动态重复div的实现方法
Got it! Let’s walk through how to add dynamic duplicate divs to your Rails form using jQuery (or vanilla JavaScript if you’d prefer) — this will let users click a plus button to add new expense rows easily:
First, structure your form to include a container for dynamic rows, an initial row, and an "Add" button. We’ll use a reusable class (expense-row) for the rows we want to duplicate:
<%= form_for @expense_fills, url: view_excess_path, method: :post do |f| %> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> <h3 class="box-title text-center">Expense Fills</h3> <!-- Add Button --> <button type="button" id="add-expense-row" class="btn btn-success pull-right">+ Add Expense</button> </div> <div class="box-body"> <!-- Container to hold all dynamic rows --> <div id="expense-rows-container"> <!-- Initial expense row (this will be cloned) --> <div class="row expense-row" style="margin-left: 20%"> <div class="col-xs-4 col-sm-4"> <%= f.text_field :amount, placeholder: "Amount", class: "form-control" %> </div> <div class="col-xs-4 col-sm-4"> <%= f.text_field :description, placeholder: "Description", class: "form-control" %> </div> <div class="col-xs-2 col-sm-2"> <button type="button" class="btn btn-danger remove-row">- Remove</button> </div> </div> </div> </div> <div class="box-footer"> <%= f.submit "Submit", class: "btn btn-primary" %> </div> </div> </div> </div> <% end %>
Add this script (either in your application.js file or a dedicated JS file) to handle cloning rows and removing them:
$(document).ready(function() { // Handle adding new expense rows $('#add-expense-row').click(function() { // Clone the first existing expense row const $newRow = $('.expense-row:first').clone(); // Clear input values so the new row is empty $newRow.find('input').val(''); // Update input names to use array syntax (critical for Rails to parse params correctly) $newRow.find('input').each(function() { const currentName = $(this).attr('name'); // Replace any existing index with empty brackets to create an array const newName = currentName.replace(/\[\d+\]/g, '[]'); $(this).attr('name', newName); // Update the ID to avoid duplicates (optional but follows HTML best practices) $(this).attr('id', `${currentName.replace(/[\[\]]/g, '_')}_${Date.now()}`); }); // Append the new row to the container $('#expense-rows-container').append($newRow); }); // Handle removing rows (use event delegation for dynamically added elements) $(document).on('click', '.remove-row', function() { // Prevent removing the last row (adjust this if you want to allow empty forms) if ($('.expense-row').length > 1) { $(this).closest('.expense-row').remove(); } else { alert('You need at least one expense row!'); } }); });
If you don’t want to use jQuery, here’s the same functionality with plain JS:
document.addEventListener('DOMContentLoaded', function() { const addButton = document.getElementById('add-expense-row'); const container = document.getElementById('expense-rows-container'); // Add new row addButton.addEventListener('click', function() { const firstRow = document.querySelector('.expense-row'); const newRow = firstRow.cloneNode(true); // Clear input values const inputs = newRow.querySelectorAll('input'); inputs.forEach(input => { input.value = ''; // Update name attribute for Rails array parsing let currentName = input.getAttribute('name'); const newName = currentName.replace(/\[\d+\]/g, '[]'); input.setAttribute('name', newName); // Update ID to avoid duplicates input.setAttribute('id', `${currentName.replace(/[\[\]]/g, '_')}_${Date.now()}`); }); container.appendChild(newRow); }); // Remove row (event delegation) document.addEventListener('click', function(e) { if (e.target.classList.contains('remove-row')) { const rows = document.querySelectorAll('.expense-row'); if (rows.length > 1) { e.target.closest('.expense-row').remove(); } else { alert('You need at least one expense row!'); } } }); });
To properly handle the submitted data in your controller, make sure you permit the array parameters. For example:
def expense_fill_params # If params[:expense_fills] is an array of hashes params.require(:expense_fills).map do |fill| fill.permit(:amount, :description) end end
If you’re using nested attributes with a parent model, you’ll also need to add accepts_nested_attributes_for :expense_fills to your parent model, and adjust the params to permit expense_fills_attributes: [:amount, :description].
内容的提问来源于stack exchange,提问作者Bharat Mane

