nyergler / nested-formset

Nest Django formsets for multi-level editing.
BSD 3-Clause "New" or "Revised" License
87 stars 21 forks source link

How to make it dynamic ? #23

Open isasiluis28 opened 7 years ago

isasiluis28 commented 7 years ago

I'm struggling with making it dynamic, like with jquery formsets, but that plugin wont work out of the box, how can I do it then ? thank you

skiv23 commented 7 years ago

Hi, this is the answer for someone like me who decided to make it dynamic.

I've faced this issue when it was required for me to build a "Bar chart builder" so that a user could place multiple charts with customizable rows on the page along with other ordinary formsets.

I've been using underscore.js since the first time I faced dynamic formsets and I came up with generic function which will be described below along with HTML code.

HTML:

{{ chart_formset.management_form }}
<div id="chart_container">
    {% for chart in chart_formset %}
    <div class="formsetForm">
        {{ chart|crispy }}
        {{ chart.nested.management_form }}
        <div id="chart_row_container_{{ forloop.counter0 }}">
            {% for chart_row in chart.nested %}
            <div class="formsetForm">
                {{ chart_row|crispy }}
            </div>
            {% endfor %}
        </div>
    <!-- data-counter is required for our function to know to which container it should append a form —>
        <input type="button" data-formset="chart_row" data-counter="{{ forloop.counter0 }}"
               value="Add Chart Row" class="btn btn-success add-formset"/>
    </div>
    {% endfor %}
</div>
<input type="button" data-formset="chart" value="Add Chart" class="btn btn-success add-formset"/>

JS:

{{ block.super }} // jQuery, etc.
{{ chart_formset.empty_form.nested.empty_form.media }}
<script src="{% static 'lib/underscore-min.js' %}"></script>
<script type="text/html" id="chart-template">
    <div class="formsetForm">
        {{ chart_formset.empty_form|crispy }}

    // we need to add a management form for each nested formset
        {{ chart_formset.empty_form.nested.management_form }}
        <div id="chart_row_container___prefix__"></div>
        <input type="button" data-formset="chart_row" data-counter="__prefix__" value="Add Chart Row" class="btn btn-success add-formset"/>
    </div>
</script>
<script>
    addFormsetForm = function (ev, formset_name) {
        ev.preventDefault();

        var formset = $(this).data('formset') || formset_name,
            formsetSplit = formset.split('_'),
            counter = $(this).data('counter'),
            $container = typeof counter != 'undefined' ? $('div#' + formset + '_container_' + counter) : $('div#' + formset + '_container'), // find the container
            count = $container.children().length,
            tmplMarkup = $('#' + formset + '-template').html(), // find the template
            compiledTmpl;

    // compile our template
        if (typeof counter != 'undefined') {
            compiledTmpl = tmplMarkup.replace(
                new RegExp('__prefix__-' + formsetSplit[1] + 's-__prefix__', 'g'),
                counter + '-' + formsetSplit[1] + 's-' + count
            ).replace(/__num__/g, count + 1);
        }
        else
            compiledTmpl = tmplMarkup.replace(/__prefix__/g, count).replace(/__num__/g, count + 1);

        $container.append(compiledTmpl);

    // bind mouse click on newly created “Add formset” button
        $container.find('.add-formset[data-counter=' + count + ']').click(addFormsetForm);

        // update form count
        var id;

        if (typeof counter != 'undefined')
            id = formsetSplit[0] + 's-' + counter + '-' + formsetSplit[1] + 's'
        else
            id = formset + 's';

        $('#id_' + id + '-TOTAL_FORMS').attr('value', count + 1);

    };

    $('.add-formset').click(addFormsetForm);
</script>