0

I'm using .clone() to duplicate a form. I make small changes to the clone and then place it after the last form. As you can see from the screenshot it's mostly working; they look identical. The application (Django/Python) can process the clone as well once I press save.

enter image description here

The problem is the calendar widget does not open when clicked (on the clone form). It does open if I click on the widget button for a form that already exists on the page when it first loads (not a clone). But on my clones the date picker does not open.

What it should look like after I click on it (instead nothing happens):

enter image description here

The cloned html seems to look identical in all the right ways.

Existing form: enter image description here

Clone: enter image description here

EDIT Event Listeners: Looking at the Event Listeners for the tag the on click event seems to me missing.

Existing Form:

enter image description here

Clone:

enter image description here

Is something missing from the cloned html? Or is there something behind the scenes that is not working? I just don't understand what is broken here.

JS/JQuery:

function cloneMore(selector, prefix,form_class) {
    var newElement = $(selector).clone(true);
    var total = $('#id_' + prefix + '-TOTAL_FORMS').val();
    newElement.find(':input:not([type=button]):not([type=submit]):not([type=reset])').each(function() {
        var name = $(this).attr('name').replace('-' + (total-1) + '-', '-' + total + '-');
        var id = 'id_' + name;
        $(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');

    });
    newElement.find('label').each(function() {
        var forValue = $(this).attr('for');
        if (forValue) {
          forValue = forValue.replace('-' + (total-1) + '-', '-' + total + '-');
          $(this).attr({'for': forValue});
        }
    });
    total++;
    $('#id_' + prefix + '-TOTAL_FORMS').val(total);
    $(selector).after(newElement);
    return false;
}

        $(document).on('click', '.add-form-row', function(e){
            e.preventDefault();
            cloneMore('.form-row-payment:last', 'payment_invoice','form-row-payment');
            return false;
        });

HTML:

 {{ payments_formset.management_form }}
    {{ payments_formset.media }}

<h3>Payments</h3>
            {% for formpay in payments_formset %}
                <div class="form-row form-row-payment row container"  name="payment_form" style="padding:0px;" id="payment_formset">
                    {{ formpay.non_form_errors }}
                    {{ formpay.non_field_errors }}

                    {% for hidden in formpay.hidden_fields %}
                    {{ hidden }}
                {% endfor %}                            
                {% for field in formpay %}
                    {% if field.name != 'index' and field.name != 'invoice'%}

                        <div class="col-sm">
                            {{ field.errors }}
                            {{ field|as_crispy_field }}
                            {% if field.help_text %}
                            <p class="help">{{ field.help_text|safe }}</p>
                            {% endif %}                            
                        </div>
                    {% endif %}
                {% endfor %} 
            </div>                    
            {% endfor %}
        <div class="input-group-append">
                    <button class="btn btn-success add-form-row">+</button>
            </div> 
Liam Hanninen
  • 1,525
  • 2
  • 19
  • 37
  • Does this answer your question? [What is the most efficient way to deep clone an object in JavaScript?](https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript) – Dane Brouwer May 08 '20 at 11:13
  • Possibly - it says Jquery .clone() only clones DOM elements. Maybe that's part of the issue. I'll try one of the alternatives suggested in that answer. – Liam Hanninen May 08 '20 at 11:17
  • Are you cloning HTML code or Javascript Objects? – Dane Brouwer May 08 '20 at 11:19
  • Not sure - I provide a class to a JQuery selector: $('.form-row-payment:last').clone(true). Not sure if that is considered HTML or a JS object behind the scenes. – Liam Hanninen May 08 '20 at 11:23
  • If it's HTML code then you can [look at the following](https://stackoverflow.com/questions/921290/is-it-possible-to-clone-html-element-objects-in-javascript-jquery) – Dane Brouwer May 08 '20 at 11:26
  • That successfully cloned it but still the calendar widget did not work. – Liam Hanninen May 08 '20 at 11:40
  • 1
    [jquery-clone-not-cloning-event-bindings-even-with-on](https://stackoverflow.com/questions/9549643/jquery-clone-not-cloning-event-bindings-even-with-on) Probalby it's not working cause of the attached events, try to use clone(true, true) – antlis May 08 '20 at 12:03
  • I think this is along the right track - BUT using clone(true, true) did not work – Liam Hanninen May 08 '20 at 12:11
  • Based on your comment, antlis, I checked and it seems that the on click event is missing - I added an Edit to the question "EDIT Event Listeners" – Liam Hanninen May 08 '20 at 12:17
  • I guess you have to clone markup for that form, and then destory onclick, and reintialize calendar for cloned form again. This one looks like the similar problem [problem-when-cloning-jquery-ui-datepicker](https://stackoverflow.com/questions/2441061/problem-when-cloning-jquery-ui-datepicker) – antlis May 08 '20 at 12:19
  • I added initialized calendar for the clone in various places but still no luck. – Liam Hanninen May 08 '20 at 12:49
  • What do you use for the calendar widget? – antlis May 08 '20 at 13:02

1 Answers1

0

You are making a copy of the HTML objects, but these do not have the same event handlers as the original object. To remedy this, I would suggest making a function that will reset the event handlers for the cloned elements.

This could be done something like this:

function resetCalendarClickHandler(){
    $(".input-group-addon").unbind("click") //remove current event handlers to prevent duplication when adding new one
    $(".input-group-addon").click(<yourEventHandler>);
}

Once you have this, you can add a call to this function at the end of your cloneMore() function and this will re-assign the event handlers and include the cloned element as it will be part of your HTML at this point.

John Martinez
  • 218
  • 1
  • 9
  • Thanks - this might do the trick but can you provide a way to retrieve the event handler from the previous/existing form dynamically? I won't necessarily know the event in each case. Is there something like $(selector).find(".input-group-addon").events (selector represents the previous/existing form)? – Liam Hanninen May 09 '20 at 07:01