Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

form Input value not updating when its model changes #547

Open
iltoga opened this issue Jul 4, 2023 · 1 comment
Open

form Input value not updating when its model changes #547

iltoga opened this issue Jul 4, 2023 · 1 comment

Comments

@iltoga
Copy link

iltoga commented Jul 4, 2023

Hi, I am trying to use django-unicorn to render a form and trying to stick as much as possible with the documentation.
This is the issue I have found:

django-unicorn component:

from django.contrib import messages
from django.db import transaction
from django.shortcuts import redirect
from django.utils import timezone
from django_unicorn.components import UnicornView

from customer_applications.models import DocApplication
from customers.models import Customer
from invoices.forms import InvoiceForm
from invoices.models import Invoice, InvoiceApplication


class InvoiceCreateView(UnicornView):
    form_class = InvoiceForm
    invoice: Invoice = None

    customer: Customer = None
    customers = Customer.objects.all()
    payment_status_choices = InvoiceApplication.PAYMENT_STATUS_CHOICES
    invoice_status_choices = Invoice.INVOICE_STATUS_CHOICES
    customer_applications = []
    invoiceapplications = []

    invoice_date = None
    due_date = None

    class Meta:
        javascript_exclude = ("customers", "customer_applications", "invoiceapplications")

    def __init__(self, *args, **kwargs):
        super().__init__(**kwargs)

    def mount(self):
        self.invoice = Invoice()
        self.invoice_date = timezone.now()
        self.due_date = timezone.now()
        self.invoiceapplications = [InvoiceApplication()]

    def select_customer(self, value, idx):
        if value:
            self.customer = Customer.objects.get(pk=value)
            self.customer_applications = DocApplication.objects.filter(customer=self.customer)

    def select_customer_application(self, value, idx):
        if value:
            customer_application = DocApplication.objects.get(pk=value)
            self.invoiceapplications[idx].amount = customer_application.price
            self.invoiceapplications = list(self.invoiceapplications)

    def add_form(self):
        self.invoiceapplications.append(InvoiceApplication())
        self.invoiceapplications = list(self.invoiceapplications)

    def remove_form(self, index):
        if len(self.invoiceapplications) > 1:
            del self.invoiceapplications[index]
            self.invoiceapplications = list(self.invoiceapplications)

    def submit(self):
        pass

    def calculate_total_amount(self):
        # TODO: Implement the calculation logic
        pass

    def calculate_due_amount(self):
        # TODO: Implement the calculation logic
        pass

    def calculate_status(self):
        # TODO: Implement the calculation logic
        pass

    def save(self):
        with transaction.atomic():
            if self.invoice.pk:
                self.invoice.updated_by = self.request.user
            else:
                self.invoice.created_by = self.request.user
            self.invoice.save()
            for invoiceapplication in self.invoiceapplications:
                invoiceapplication.invoice = self.invoice
                invoiceapplication.save()
        messages.success(self.request, "Invoice saved successfully.")

        return redirect(f"/invoices/detail/{self.invoice.pk}/")

html template:

<div>
    <h2 class="text-center my-4">Create a new Invoice</h2>
    <!-- NOTE that this form uses bootstrap5 classes whenever possible -->
    <form method="post" class="p-3 rounded col-md-12" unicorn:submit.prevent="submit">
        <!-- Customer dropdown -->
        <div class="col-md-6" unicorn:ignore>
            <label for="customer" class="form-label">Customer</label>
            <select
                name="customer"
                id="customer_id"
                class="form-select select2"
                unicorn:model="customer"
                onchange="Unicorn.call('invoice_create', 'select_customer', this.value, this.selectedIndex);"
            >
                {% for customer in customers %}
                <option value="{{ customer.pk }}">{{ customer }}</option>
                {% endfor %}
            </select>
        </div>
        <!-- Parent column -->
        <div class="col-md-6">
            <!-- New row for two date fields -->
            <div class="row">
                <!-- Invoice Date -->
                <div class="col-md-3">
                    <label for="invoice_date" class="form-label">Invoice Date</label>
                    <input unicorn:model="invoice_date" type="date" class="form-control">
                </div>
                <!-- Due Date -->
                <div class="col-md-3">
                    <label for="due_date" class="form-label">Due Date</label>
                    <input unicorn:model="due_date" type="date" class="form-control">
                </div>
            </div>
        </div>
        <!-- Status Dropdown -->
        <div class="col-md-3">
            <label for="status" class="form-label">Status</label>
            <select class="form-select" unicorn:model="status">
                {% for status in invoice_status_choices %}
                <option value="{{ status.0 }}">{{ status.1 }}</option>
                {% endfor %}
            </select>
        </div>
        <!-- Notes -->
        <div class="col-md-6">
            <label for="notes" class="form-label">Notes</label>
            <textarea unicorn:model="notes" class="form-control" id="notes" rows="3"></textarea>
        </div>
        <!-- Total Amount (calculated) -->
        <div class="col-md-3">
            <label for="total_amount" class="form-label">Total Amount</label>
            <input unicorn:model="total_amount" type="number" class="form-control" disabled>
        </div>
        <!-- Invoice Applications -->
        <div>
            <h3 class="text-start my-4">Invoice Applications</h3>
        </div>
        <div>
            {% for invoice_application in invoiceapplications %}
            <div class="p-3 col-md-6 rounded bordered">
                <!-- Customer Application dropdown -->
                <div class="col">
                    <label for="customer_application" class="form-label">Customer Application</label>
                        <select
                        class="form-select"
                        unicorn:model="invoice_application.customer_application"
                        onchange="Unicorn.call('invoice_create', 'select_customer_application', this.value, {{forloop.counter0}});"
                    >
                        {% for customer_application in customer_applications %}
                        <option value="{{ customer_application.pk }}">{{ customer_application }}</option>
                        {% endfor %}
                    </select>

                </div>
                <!--    Invoice Date -->
                <div class="col-md-3">
                    <label for="payment_date" class="form-label">Payment Date</label>
                    <input unicorn:model="invoice_application.payment_date" type="date" class="form-control">
                </div>
                <!-- Amount -->
                <div class="col-md-3">
                    <label for="amount" class="form-label">Amount</label>
                    <input unicorn:model="invoice_application.amount" type="number" class="form-control">
                    <p>{{invoice_application.amount}}</p>
                </div>
                <!-- Payment Status Dropdown -->
                <div class="col-md-3">
                    <label for="payment_status" class="form-label">Payment Status</label>
                    <select class="form-select" unicorn:model="invoice_application.payment_status">
                        {% for payment_status in payment_status_choices %}
                        <option value="{{ payment_status.0 }}">{{ payment_status.1 }}</option>
                        {% endfor %}
                    </select>
                </div>
                <!-- Notes -->
                <div class="col">
                    <label for="notes" class="form-label">Notes</label>
                    <textarea unicorn:model="invoice_application.notes" class="form-control" id="notes" rows="3"></textarea>
                </div>
                <div class = "col-md-6 mt-3">
                    <button class="btn btn-sm btn-danger" type="button" unicorn:click="remove_form({{ forloop.counter0 }})">Remove</button>
                </div>
            </div>
            {% endfor %}
            <button class="btn btn-secondary" type="button" unicorn:click="add_form">Add Application</button>
        </div>
        <div>
            <hr \>
            <button type="submit" class="btn btn-primary">Create</button>
        </div>
    </form>
</div>

when select the Customer Application dropdown it correctly updates this element:

<p>{{invoice_application.amount}}</p>

but not its relative input field:

<input unicorn:model="invoice_application.amount" type="number" class="form-control">

For now I resolved it by adding the value attribute to the input field:

<input unicorn:model="invoice_application.amount" type="number" class="form-control" value="{{invoice_application.amount}}">

but I believe django-unicorn should update the value automatically. Am I wrong?

Thank you for any help.

@iltoga
Copy link
Author

iltoga commented Jul 4, 2023

other than that it would be really lovely to be able to use {% form | crispy %} with unicorn model instead of having to manually write all this html ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant