0

I want to dynamically add a form to my Django formset. However, on button click nothing happens. I am using form models, but I am defining most elements within the template. I know this is not proper practice, but it was how I originally created my forms and I have not had time to migrate to complete form models. However, could this be causing my issues? I have never used formsets before so any tips would be greatly appreciated! I used Dave's solution here, but I am unsure as to why my implementation does not translate accordingly.

template.html

<div class="container">
    <div id="form_set">
        <form action="{% url 'presales' %}" class="presales_formset" data-total-url="{% url 'presales_total' %}" id="presalesForm"method="post" name="presalesForm">
            <div class="field">
                <label class="label is-large">High Level Task List</label>
            </div>
            {% csrf_token %}

            {{ presales_formset.management_form }}

            {% for presales_form in presales_formset %}
                <div class="section">
                    <div class="field">
                        <label class="label">Opportunity</label>
                        <div class="select">
                            <select id="select_opportunity" name="select_opportunity">
                                <option value="">Opportunity</option>
                                {% for opportunity in my_opportunities %}
                                    <option id="selected_opportunity" name="selected_opportunity" value="{{ opportunity.name }}">{{ opportunity.name }}</option>
                                {% endfor %}
                            </select>
                        </div>
                    </div>
                    <label class="label">Task Description:</label>
                    <div class="field">
                        <div class="control">
                            <input class="input" id="task_description" name="task_description" placeholder="Task Description">
                        </div>
                    </div>
                    <label class="label">Hours</label>
                    <div class="field">
                        <div class="control">
                            <input class="input" id="hours" name="hours" placeholder="Hours">
                        </div>
                    </div>
                    <label class="label">Engineer Level:</label>
                    <div class="field">
                        <div class="select">
                            <select id="select_engineer_level" name="select_engineer_level">
                                <option value="">Engineer Level</option>
                                <option value="PM">PM</option>
                                <option value="Solutions Technician">Solutions Technician</option>
                                <option value="Solutions Engineer">Solutions Engineer</option>
                                <option value="Senior Solutions Engineer">Senior Solutions Engineer</option>
                                <option value="Solutions Architect">Solutions Architect</option>
                            </select>
                        </div>
                    </div>
                </div>
            {% endfor %}
        </form>
    </div>
    <div id="empty_form" style="display: none;">
        <form action="{% url 'presales' %}" class="presales_formset" data-total-url="{% url 'presales_total' %}" id="emptyPresalesForm" method="post" name="presalesForm">
            {% csrf_token %}

            {{ presales_formset.empty_form }}

            <div class="section">
                <div class="field">
                    <label class="label">Opportunity</label>
                    <div class="select">
                        <select id="empty_select_opportunity" name="select_opportunity">
                            <option value="">Opportunity</option>
                            {% for opportunity in my_opportunities %}
                                <option id="empty_selected_opportunity" name="selected_opportunity" value="{{ opportunity.name }}">{{ opportunity.name }}</option>
                            {% endfor %}
                        </select>
                    </div>
                </div>
                <label class="label">Task Description:</label>
                <div class="field">
                    <div class="control">
                        <input class="input" id="empty_task_description" name="task_description" placeholder="Task Description">
                    </div>
                </div>
                <label class="label">Hours</label>
                <div class="field">
                    <div class="control">
                        <input class="input" id="empty_hours" name="hours" placeholder="Hours">
                    </div>
                </div>
                <label class="label">Engineer Level:</label>
                <div class="field">
                    <div class="select">
                        <select id="empty_select_engineer_level" name="select_engineer_level">
                            <option value="">Engineer Level</option>
                            <option value="PM">PM</option>
                            <option value="Solutions Technician">Solutions Technician</option>
                            <option value="Solutions Engineer">Solutions Engineer</option>
                            <option value="Senior Solutions Engineer">Senior Solutions Engineer</option>
                            <option value="Solutions Architect">Solutions Architect</option>
                        </select>
                    </div>
                </div>
            </div>
        </form>
    </div>

    <div class="field is-inline-flex">
        <div class="control">
            <button class="button is-success" id="add_more" type="button">+</button>
        </div>
    </div>

script.js

<script>
    $('#add_more').click(function () {
        var form_idx = $('#presalesForm-TOTAL_FORMS').val();
        $('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
        $('#presalesForm-TOTAL_FORMS').val(parseInt(form_idx) + 1);
    });
</script>

forms.py

# special field names
TOTAL_FORM_COUNT = 'TOTAL_FORMS'
INITIAL_FORM_COUNT = 'INITIAL_FORMS'
MIN_NUM_FORM_COUNT = 'MIN_NUM_FORMS'
MAX_NUM_FORM_COUNT = 'MAX_NUM_FORMS'
ORDERING_FIELD_NAME = 'ORDER'
DELETION_FIELD_NAME = 'DELETE'

# default minimum number of forms in a formset
DEFAULT_MIN_NUM = 0

# default maximum number of forms in a formset, to prevent memory exhaustion
DEFAULT_MAX_NUM = 1000


class PresalesForm(forms.Form):
    class Meta:
        model = Presales
        fields = ('selected_opportunity', 'task_description', 'hours', 'selected_engineer_level', 'total_price')
garmars
  • 129
  • 1
  • 12

1 Answers1

0

Seeing as this appears to be html code that is injected into an existing page, and the JavaScript does not appear to be injected together with it, I would suspect that the issue is with the JavaScript code not finding that button.

Try the following code:

<script>
    $(document).ready(function () {
        $('body').on('click', '#add_more', function () {
            var form_idx = $('#presalesForm-TOTAL_FORMS').val();
            $('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
            $('#presalesForm-TOTAL_FORMS').val(parseInt(form_idx) + 1);
        });
    });
</script>

What happens here is that I am attaching a click listener to the body tags (seeing as I assume it is the parent of the container div), and add a selector that will be the one that this event is triggered for.

.on can be used on dynamically added elements that did not exist when the script was loaded, as opposed to .click.

For further reading regarding .on function.
Differences between .on('click') and .click().

BoobyTrap
  • 967
  • 7
  • 18
  • Can you try to add a `consoloe.log('test print');` inside that `on` listener and see if it gets triggered by the clicking? – BoobyTrap Jul 26 '19 at 13:43
  • yeah, it isn't being triggered onClick – garmars Jul 26 '19 at 14:20
  • Inspect the page and check if the script code is on the page. It makes no sense that it isn't triggered when the button exists. – BoobyTrap Jul 26 '19 at 14:23
  • I see this error `ReferenceError: $ is not defined` The script is on the page but greyed out. When I look at the script, it says no element is selected. – garmars Jul 26 '19 at 14:25
  • I edited the answer, try to add what I added - `$(document).ready( function () {` (don't forget to close the brackets too). This will make sure the script is ran only once the page finished loading. Also make sure you have JQuery added on you page. – BoobyTrap Jul 26 '19 at 14:31
  • 1
    it is working now, I was loading my script before I loaded jQuery. Thanks! – garmars Jul 26 '19 at 14:45