Как обработать JavaScript событие внутри inlineformset_factory с formset_media_js - PullRequest
0 голосов
/ 05 апреля 2020

У меня есть inlineformset_factory, реализованная с помощью formset_media_ js, эти два сами по себе работают нормально. Что мне нужно реализовать, так это уметь обрабатывать состояние включения и отключения некоторых флажков и полей ввода, которые находятся внутри inlineformset_factory.

У меня есть javascript, который работает с первой группой набора форм, созданной в загрузка страницы, но когда пользователь добавляет новый набор форм, javascript не работает.

Как я могу обрабатывать поля ввода новых наборов форм, добавленные пользователем с помощью javascript?

Если установлен флажок «is chapter», то «is subchapter» и «amount» отключены, по умолчанию inlineformset_fatory создает 1 набор форм при загрузке страницы, для этого набора форм работает javascript. Но когда пользователь добавляет другой набор форм с помощью кнопки «Добавить еще один элемент бюджета», javascript больше не работает. Если, например, я настраиваю inlineformser_factory для создания 3 наборов форм при загрузке страницы, javascript работает на этих 3 наборах форм, но не на наборах форм, добавленных пользователем.

enter image description here

forms.py : в этом forms.py у меня есть inlineformset_factory, которая создается каждый раз, когда пользователь добавляет набор форм.

from django import forms
from django.forms import inlineformset_factory

from djangoformsetjs.utils import formset_media_js
from accounts.models import ProjectManager
from projects.models import Project, BudgetModel, BudgetModelItems


class CreateBudgetItem(forms.ModelForm):
    class Media(object):
        js = formset_media_js

    class Meta:
        model = BudgetModelItems
        fields = ('budget_model',)
        widgets = {
            'budget_item_description': forms.Textarea(attrs={'rows': 2, 'cols': 50}),
            'budget_item_item': forms.NumberInput(attrs={'size': 6}),
            'budget_item_quantity': forms.NumberInput(attrs={'size': 6}),
        }


BudgetItemFormset = inlineformset_factory(BudgetModel, BudgetModelItems,
                                          form=CreateBudgetItem,
                                          fields=('budget_model', 'budget_item_item',
                                                  'budget_item_description', 'budget_item_unit',
                                                  'budget_item_quantity', 'budget_item_is_chapter',
                                                  'budget_item_is_subchapter'),
                                          extra=1,
                                          can_delete=True,
                                          can_order=True
                                          )

views.py

from django.shortcuts import render, redirect
from django.forms import formset_factory

from accounts.models import ProjectManager
from projects.forms.create_project import CreateProjectForm
from projects.forms.create_budgetmodel import BudgetFormset, ProjectForBudgetModel
from projects.forms.create_budgetitem import CreateBudgetItem, BudgetItemFormset
from projects.models import BudgetModel, Project


def create_budget_item(request):
    user = request.user.projectmanager
    projects = Project.objects.filter(project_manager_id=user)
    models = BudgetModel.objects.none()

    project_form = ProjectForBudgetModel(user)
    budget_item_form = CreateBudgetItem()
    formset = BudgetItemFormset()

    for project in projects:
        models |= BudgetModel.objects.filter(project_id=project.pk)
        budget_item_form.fields['budget_model'].queryset = models

    if request.method == 'POST':
        project_form = ProjectForBudgetModel(user, request.POST)
        budget_item_form = CreateBudgetItem(request.POST)
        if project_form.is_valid() and budget_item_form.is_valid():
            # project_id = project_form.cleaned_data['project']
            budget_model_id = budget_item_form.cleaned_data['budget_model']
            formset = BudgetItemFormset(request.POST, instance=budget_model_id)
            if formset.is_valid():
                formset.save()

    context = {'project_form': project_form,
               'bi_form': budget_item_form,
               'formset': formset,
               'models': models}
    return render(request, 'projects/create_budget_items.html', context)

budget_item_form. html: эта форма вызывается (включается) в create_budget_items. html

<div data-formset-form>
    <div class="card">
        <div class="card-body">
            <div class="row">
                <div class="col">
                    <table class="table">
                        <thead class="thead-light">
                        <tr>
                            <th scope="col">Item</th>
                            <th scope="col">Description</th>
                            <th scope="col">Unit</th>
                            <th scope="col">Quantity</th>
                            <th scope="col">Is Chapter</th>
                            <th scope="col">Is SubChapter</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <th>{{ form.budget_item_item }}</th>
                            <td>{{ form.budget_item_description }}</td>
                            <td>{{ form.budget_item_unit }}</td>
                            <td>{{ form.budget_item_quantity }}</td>
                            <td>{{ form.budget_item_is_chapter }}</td>
                            <td>{{ form.budget_item_is_subchapter }}</td>
                        </tr>
                        </tbody>
                    </table>
                </div>
                <div class="col-md-auto">
                    {% if form.ORDER %}
                        <div class="row mt-1">
                            <div class="d-none">{{ form.ORDER }}</div>
                            <button class="btn btn-info btn-block" type="button" data-formset-move-up-button>
                                {% trans 'Move up' %}
                            </button>
                        </div>
                        <div class="row mt-1">
                            <button class="btn btn-info btn-block" type="button" data-formset-move-down-button>
                                {% trans 'Move down' %}
                            </button>
                        </div>
                    {% endif %}
                </div>
                <div class="col col-lg-2 mt-1">
                    {% if form.DELETE %}
                        <div class="d-none">{{ form.DELETE }}</div>
                        <button type="button" class="btn btn-danger btn-block h-100" data-formset-delete-button>
                            {% trans 'Delete' %}
                        </button>
                    {% endif %}
                </div>
            </div>
        </div>
    </div>
</div>

create_budget_items. html: В этом шаблоне у меня есть javascript, где я управляю включением и отключением состояний флажков и полей ввода. Я подумал, что, вызвав скрипт внутри для l oop, где повторяется набор форм, я смог управлять полями ввода наборов форм, добавленных пользователем. Работает только с наборами форм, созданными при загрузке страницы.

{% block dashboard_head %}
    {{ formset.media }}
    <script type="text/javascript">
        function trackDisabled(trigger_id, ...targets) {
            const checkbox = document.getElementById(trigger_id);
            checkbox.addEventListener('change', e => {
                console.log(e.target.checked);
                {#console.log(trigger_id);#}
                {#console.log(...targets);#}
                if (e.target.checked === true) {
                    targets.forEach(x => {
                        const element = document.getElementById(x);
                        element.disabled = true;
                        element.checked = false;
                        element.value = ''
                    })
                } else {
                    targets.forEach(x => document.getElementById(x).disabled = false)
                }
            })
        }
    </script>

{% endblock dashboard_head %}

{% block dashboard_content %}
    <h1>Create Budget Items</h1>
    <form method="post">
        {% csrf_token %}
        {{ project_form.project }}

        <select name="budget_model" id="id_budget_model" class="form-control">
            {% with value=bi_form.budget_model.value %}
                {% for model in models %}
                    <option value="{{ model.pk }}" class="{{ model.project.pk }}"
                            {% if model.pk|slugify == value|slugify %}selected="selected"{% endif %}>
                        {{ model.budget_name }}
                    </option>
                {% endfor %}
            {% endwith %}
        </select>

        {% load formset_tags %}
        <div id="formset" data-formset-prefix="{{ formset.prefix }}">
            {{ formset.management_form }}
            <div data-formset-body>
                {% for form in formset %}
                    {% include "projects/budget_item_form.html" with form=form only %}
                    <script>
                        trackDisabled(
                            '{{ form.budget_item_is_chapter.auto_id }}',
                            '{{ form.budget_item_is_subchapter.auto_id }}',
                            '{{ form.budget_item_quantity.auto_id }}'
                        );
                        console.log('{{ form.budget_item_is_chapter.auto_id }}');
                    </script>
                    {{ form.errors }}
                {% endfor %}
            </div>
            <script type="form-template" data-formset-empty-form>
                {% escapescript %}
                    {% include "projects/budget_item_form.html" with form=formset.empty_form only %}
                {% endescapescript %}
            </script>
            <div class="row mt-3 mr-1 ml-1">
                <!-- This button will add a new form when clicked -->
                <div class="col text-center">
                    <input class="w-75 btn btn-info" type="button"
                           value="{% trans 'Add another Budget Item' %}" data-formset-add>
                </div>
                <div class="col text-center">
                    <button class="w-75 btn btn-success" type="submit">
                        {% trans 'Create Models' %}
                    </button>
                </div>
            </div>
        </div>
    </form>

{% endblock dashboard_content %}

1 Ответ

0 голосов
/ 07 апреля 2020

Это javascript, который, наконец, сработал, был написан другом .. Спасибо FunkyBob!

<script>
    function isChapter() {
        const root = document.getElementById('formset');
        const prefix = root.dataset.formsetPrefix;
        console.log({root, prefix});
        // listen for all input changes
        root.addEventListener('change', ev => {
            // check if it matches out name pattern
            console.log(ev.target.name);
            console.log(ev.target.checked, !ev.target.checked);
            console.log(`${prefix}-(\\d+)-budget_item_is_chapter`);
            let m = ev.target.name.match(RegExp(`${prefix}-(\\d+)-budget_item_is_chapter`));
            // if it's not {prefix}-*-budget_item_is_chapter ignore
            if (!m) return;
            console.log(m);
            let idx = m[1]; // the matched regex group
            // Find the related fields, and set them as enabled/disabled
            root.querySelector(`[name="${prefix}-${idx}-budget_item_is_subchapter"]`).disabled = ev.target.checked;
            root.querySelector(`[name="${prefix}-${idx}-budget_item_is_subchapter"]`).checked = false;
            root.querySelector(`[name="${prefix}-${idx}-budget_item_unit"]`).disabled = ev.target.checked;
            root.querySelector(`[name="${prefix}-${idx}-budget_item_unit"]`).value = ev.target.checked;
            root.querySelector(`[name="${prefix}-${idx}-budget_item_quantity"]`).disabled = ev.target.checked;
            root.querySelector(`[name="${prefix}-${idx}-budget_item_quantity"]`).value = ev.target.checked;
            console.log("Done!")
        });
    }
    isChapter();
</script>
...