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

[Bug] Child component is not reactive #738

Open
2 tasks done
sergiopmdeveloper opened this issue Nov 7, 2024 · 1 comment
Open
2 tasks done

[Bug] Child component is not reactive #738

sergiopmdeveloper opened this issue Nov 7, 2024 · 1 comment
Labels
bug Something isn't working

Comments

@sergiopmdeveloper
Copy link

sergiopmdeveloper commented Nov 7, 2024

Bug Description

I have a for loop inside a component that renders multiple times a child component (a table with rows). These rows contain a button that updates the state of the component to show a modal in a reactive way. This interaction works, what is not displayed reactively is the modal when the state has changed. I have tried everything but nothing works.

The property that does not work in a reactive way is displayed_modal, and I can assure you that the change from False to True does occur because I have debugged it. It is not a problem with the interface either, because when you load the page with this status set to True for testing purposes, the modals are displayed. What doesn't work is the reactivity.

Expected behaviour

The modal is displayed in a reactive manner based on the change of state.

Screenshots / Screenrecords

Parent component:

from django.db.models.query import QuerySet
from django_unicorn.components import UnicornView

from apps.passwords.models import Password
from authentication.models import AppUser

class PasswordTableView(UnicornView):
"""
Password table component
"""

user_passwords: QuerySet[Password]

def mount(self) -> None:
    """
    Mounts the component
    """

    self.load_passwords()

def load_passwords(self) -> None:
    """
    Loads passwords
    """

    user: AppUser = self.request.user

    self.user_passwords = user.passwords.all()

Parent template:

{# Password table component #}

{% load unicorn %}

Passwords

        <button class="text-xs text-white bg-gray-900 p-2 rounded-md transition-colors hover:bg-gray-900/90">
            Add password
        </button>

    </div>

    <div class="mt-4 rounded-md shadow-md overflow-x-auto">
        <table class="w-full text-left">
            <thead class="text-xs text-white bg-gray-900 uppercase">
                <tr>
                    <th class="px-6 py-3">Name</th>
                    <th class="px-6 py-3">Password</th>
                    <th class="px-6 py-3">Username</th>
                    <th class="px-6 py-3">Email</th>
                    <th class="px-6 py-3">Site</th>
                    <th class="px-6 py-3">Actions</th>
                </tr>
            </thead>

            <tbody class="text-sm">
                {% if not user_passwords %}
                    <tr>
                        <td class="px-6 py-4" colspan="6">No passwords found</td>
                    </tr>
                {% endif %}

                {% for password in user_passwords %}
                    {% unicorn 'password-row' password=password key=password.id %}
                {% endfor %}
            </tbody>
        </table>
    </div>
</div>

Child component:

from django_unicorn.components import UnicornView

from apps.passwords.components.password_table import PasswordTableView
from apps.passwords.models import Password

class PasswordRowView(UnicornView):
"""
Password table component
"""

password: Password
displayed_modal = False

def display_modal(self) -> None:
    """
    Displays a modal

    Parameters
    ----------
    modal_id : str
        The id of the modal to display
    """

    self.displayed_modal = True

def hide_modal(self) -> None:
    """
    Hides the modal
    """

    self.displayed_modal = False

def delete_password(self, password_id: int) -> None:
    """
    Deletes a password
    """

    password = Password.objects.get(id=password_id)
    password.delete()

    parent: PasswordTableView = self.parent
    parent.load_passwords()

Child template:

{{ password.name }}
<td class="px-6 py-4">
    <div class="flex items-center gap-5">
        <p>**********</p>

        <button class="w-[50px] text-xs text-gray-700 bg-gray-300 py-0.5 rounded-md transition-colors hover:bg-gray-300/80"
                id="copy-password-button"
                data-password="{{ password.password }}">Copy</button>
    </div>
</td>

<td class="px-6 py-4">{{ password.username|default:"..." }}</td>
<td class="px-6 py-4">{{ password.email|default:"..." }}</td>

{% if password.site %}
    <td class="px-6 py-4">
        <a class="underline text-blue-500"
           href="{{ password.site }}"
           target="_blank">{{ password.site }}</a>
    </td>
{% else %}
    <td class="px-6 py-4">...</td>
{% endif %}

<td class="px-6 py-4">
    <div class="flex items-center gap-1">
        <button class="text-xs text-yellow-900 bg-yellow-400 px-1.5 py-1 rounded-md transition-colors hover:bg-yellow-300/90">
            Edit
        </button>

        <button unicorn:click.stop="display_modal"
                class="text-xs text-red-900 bg-red-400 px-1.5 py-1 rounded-md transition-colors hover:bg-red-300/90">
            Delete
        </button>
    </div>
</td>

{% if displayed_modal %}
    <div class="fixed top-0 left-0 w-full h-screen flex items-center justify-center bg-black/20 z-50">
        <div class="bg-white rounded-md shadow-lg">
            <div class="flex items-center justify-between p-4 border-b">
                <h3 class="text-xl font-bold">Warning</h3>

                <button unicorn:click="hide_modal"
                        class="w-8 h-8 flex items-center justify-center rounded-lg hover:bg-gray-100">
                    <svg class="w-5"
                         fill="none"
                         stroke-width="1.5"
                         stroke="currentColor"
                         viewBox="0 0 24 24"
                         xmlns="http://www.w3.org/2000/svg">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
                    </svg>
                </button>
            </div>

            <div class="p-4 border-b border-gray-200">
                <p>
                    You are about to delete the password <strong>{{ password.name }}</strong>. Are you sure you want to proceed?
                </p>
            </div>

            <div class="flex items-center gap-2 p-4">
                <button unicorn:click="delete_password({{ password.id }})"
                        unicorn:refresh
                        unicorn:loading.attr="disabled"
                        unicorn:loading.class="opacity-50 cursor-not-allowed"
                        unicorn:loading.class.remove="hover:bg-red-500/90"
                        class="w-28 flex items-center justify-center gap-1 text-white bg-red-500 py-2.5 rounded-md transition-colors hover:bg-red-500/90"
                        id="delete-password-{{ password.id }}">
                    Delete

                    <svg unicorn:target="delete-password-{{ password.id }}"
                         unicorn:loading.class.remove="hidden"
                         class="hidden w-5 animate-spin"
                         fill="none"
                         stroke-width="1.5"
                         stroke="currentColor"
                         viewBox="0 0 24 24"
                         xmlns="http://www.w3.org/2000/svg">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99" />
                    </svg>
                </button>

                <button unicorn:click="hide_modal"
                        class="flex items-center justify-center gap-1 text-white bg-gray-900 px-5 py-2.5 rounded-md transition-colors hover:bg-gray-900/90">
                    Cancel
                </button>
            </div>
        </div>
    </div>
{% endif %}

Steps to reproduce

No response

What browsers are you seeing the problem on?

No response

👀 Have you checked for similar open issues?

  • I checked and didn't find similar issue

Code of Conduct

  • I agree to follow this project's Code of Conduct

Are you willing to work on this issue ?

None

@sergiopmdeveloper sergiopmdeveloper added the bug Something isn't working label Nov 7, 2024
@sergiopmdeveloper
Copy link
Author

IMPORTANT

Is related to the use of table tags (th, td and tr) in a child component. When I replace them with normal tags like div, h1, p or span it works perfectly. So this is a bug related to the table tags!!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant