themesberg / flowbite

Open-source UI component library and front-end development framework based on Tailwind CSS
https://flowbite.com
MIT License
8.02k stars 747 forks source link

Modal Dialog not showing when navigating back to the same route using Router Outlet (Angular) #850

Open dancrts opened 8 months ago

dancrts commented 8 months ago

Describe the bug I'm using a modal dialog via the data-modal-toggle attribute, which only works fine when opening the project on the specific route that contains those modals. But when navigating to another route, it does not open again. I have to either reload the page or stop the Angular Cli and re-run it again.

To Reproduce Steps to reproduce the behavior:

  1. Create an Angular App with router outlet
  2. Add a subpage/route that contains the modal component
  3. create a table with several inserts that contain a button (In this case the modal component)
  4. create a separated component for the modal, which will be called on every row of the table with a different ID every time.
  5. asign IDs throw an @input variable to the component.
  6. Open that page specifically
  7. Verify all the modals work and on the Inspector of the browser verify both the Div with the modal, every attribute Data-modal-toggle and so have the specifical ID so IDs are Unique.
  8. Navigate to another route
  9. Navigate back to the page that has the table with the modal buttons
  10. try to open any of the modals.

Expected behavior Buttons should work, having navigated outside the component, each and every time you navigate back without having to reload the page or rerun the angular CLI

Desktop (please complete the following information):

Additional context While loading the page and inspecting it, every ID is unique. Modals only work when you havent navigated to another route, and if you do, you have to reload more than once. Specifically 2 or 3 times. First time it wont work.

I do have a router subscription on my App.component.ts file and I do have implemented the Tailwind config file and Angular.json file according to the guide provided by flowbite.

Here's the source code of the components that have the problem, the table component, the modal component with its respective TS files. Theres a Delete button that also does not work, but i have the same implementation of the Edit component, so once both should work... right?

I can paste a link to my repo if needed

users.component.html


<div class="p-4 border-2 border-gray-200 bg-white rounded">
    <h2 class="mb-10 text-3xl font-extrabold leading-none tracking-tight text-gray-900 md:text-4xl">Team management</h2>
    <p-tabView [(activeIndex)]="activeIndex">
        <p-tabPanel header="All Users">
            <div class="m-4">
                <user-edit (userInfo)="handleEdition($event, false)"></user-edit>
            </div>
            <p-table *ngIf="users" [value]="users" [paginator]="true" [rows]="5" [showCurrentPageReport]="true" [tableStyle]="{ 'min-width': '50rem' }" currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries" [rowsPerPageOptions]="[5, 10, 20]">
                <ng-template pTemplate="header">
                    <tr>
                        <th >Name</th>
                        <th>Email</th>
                        <th >Date Joined</th>
                        <th >Role</th>
                        <th >Actions</th>
                    </tr>
                </ng-template>
                <ng-template pTemplate="body" let-user>
                    <tr>
                        <td>{{ user.fullname }}</td>
                        <td>{{ user.email }}</td>
                        <td>{{ user.createdAt }}</td>
                        <td>{{ user.role }}</td>
                        <td>
                            <user-delete [user]="user" (deleteUser)="handleDeletion($event)"></user-delete>&nbsp;<user-edit [userToEdit]="user" [editMode]="true" (userInfo)="handleEdition($event, true)"></user-edit>
                        </td>
                    </tr>
                </ng-template>
                <ng-template pTemplate="paginatorleft">
                    <p-button type="button" icon="pi pi-plus" styleClass="p-button-text"></p-button>
                </ng-template>
                <ng-template pTemplate="paginatorright">
                    <p-button type="button" icon="pi pi-cloud" styleClass="p-button-text"></p-button>
                </ng-template>
            </p-table>
        </p-tabPanel>
        <p-tabPanel header="History">
            <p>
                Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium,
                totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae
                dicta sunt explicabo. Nemo enim
                ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni
                dolores eos qui ratione voluptatem sequi nesciunt. Consectetur, adipisci velit, sed quia non numquam
                eius modi.
            </p>
        </p-tabPanel>
    </p-tabView>
</div>

users.component.ts

import { Component, OnDestroy, OnInit } from '@angular/core';
import { UserService } from './users.service';
import { Subscription } from 'rxjs';
import { User } from './user.model';

@Component({
    selector: 'app-users',
    templateUrl: './users.component.html',
    styleUrl: './users.component.scss'
})
export class UsersComponent implements OnInit, OnDestroy {

    userSubscription: Subscription;

    activeIndex = 0;
    //this is just for demo purposes, i will refactor later for an actual model and an acual usage, not this...
    users: User[];

    constructor(private userService: UserService) {}

    ngOnInit(): void {
        this.userService.getUsers();
        this.userSubscription = this.userService.usersChange.subscribe(users => this.users = users);
    }

    ngOnDestroy(): void {
        this.userSubscription.unsubscribe();
    }

    handleDeletion(user: any){
        this.userService.deleteUser(user);
    }

    handleEdition(user: User, edition: boolean) {
        if(edition) {
            this.userService.updateUser(user);
        } else {
            this.userService.createUser(user)
                .subscribe(
                    data => {
                        console.log(data.description)
                        this.users.push(user);
                        this.userService.usersChange.next(this.users)
                    }
                )
        }
    }
}

edit.component.html


<button type="button" [attr.data-modal-target]="buttonsDataToggleID" [attr.data-modal-toggle]="buttonsDataToggleID"
    (click)="onShowModal()"
    class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center ">
    {{ editMode ? 'Edit User' : 'Create User'}}
</button>

<!-- Main modal -->
<div [id]="buttonsDataToggleID" tabindex="-1" aria-hidden="true"
    class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
    <div class="relative p-4 w-full max-w-md max-h-full">
        <!-- Modal content -->
        <div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
            <!-- Modal header -->
            <div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
                <h3 class="text-lg font-semibold text-gray-900 dark:text-white">
                    {{ editMode ? 'Edit' : 'Create new'}} User
                </h3>
                <button type="button" [attr.data-modal-toggle]="buttonsDataToggleID"
                    class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center">
                    <svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
                        viewBox="0 0 14 14">
                        <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                            d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
                    </svg>
                    <span class="sr-only">Close modal</span>
                </button>
            </div>
            <!-- Modal body -->
            <form class="p-4 md:p-5" [formGroup]="userForm" (ngSubmit)="onSubmit()">
                <div class="grid gap-4 mb-4 grid-cols-2">
                    <div class="col-span-2">
                        <label for="fullname" class="block mb-2 text-sm font-medium text-gray-900">Name</label>
                        <input type="text" id="fullname" formControlName="fullname" placeholder="Type user full name"
                            class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5">
                    </div>
                    <div class="col-span-2">
                        <label for="email" class="block mb-2 text-sm font-medium text-gray-900">Email</label>
                        <input type="email" id="email" formControlName="email" placeholder="johndoe@example.com"
                            class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5">
                    </div>
                    <div class="col-span-2">
                        <label for="password" class="block mb-2 text-sm font-medium text-gray-900">Password</label>
                        <input type="text" id="password" formControlName="password"
                            placeholder="Create a password for that user"
                            class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5">
                    </div>
                    <div class="col-span-2 sm:col-span-1">
                        <label for="createdAt" class="block mb-2 text-sm font-medium text-gray-900">Date Joined</label>
                        <input type="date" id="createdAt" formControlName="createdAt"
                            class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5">
                    </div>
                    <div class="col-span-2 sm:col-span-1">
                        <label for="category" class="block mb-2 text-sm font-medium text-gray-900">Category</label>
                        <select id="category" formControlName="role"
                            class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5">
                            <option selected="">Select a role</option>
                            <option value="usuario">User</option>
                            <option value="admin">Admin</option>
                        </select>
                    </div>
                </div>
                <button type="submit" [attr.data-modal-toggle]="buttonsDataToggleID" [disabled]="!userForm.valid"
                    [ngClass]="userSubmitButton.disabled ? 'bg-blue-500' : 'bg-blue-700 hover:bg-blue-800'"
                    class="text-white inline-flex items-center focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center"
                    #userSubmitButton>
                    <svg class="me-1 -ms-1 w-5 h-5" fill="currentColor" viewBox="0 0 20 20"
                        xmlns="http://www.w3.org/2000/svg">
                        <path fill-rule="evenodd"
                            d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z"
                            clip-rule="evenodd"></path>
                    </svg>
                    {{ editMode ? 'Save this' : 'Add new'}} user
                </button>
            </form>
        </div>
    </div>
</div>

edit.component.ts


import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormControl, FormGroup, NgForm, Validators } from '@angular/forms';
import { User } from '../user.model';
import { Modal } from 'flowbite';

@Component({
    selector: 'user-edit',
    templateUrl: './edit.component.html',
    styleUrl: './edit.component.scss'
})
export class UserEditComponent implements OnInit {

    userForm: FormGroup;

    @Input() editMode: boolean = false;
    @Input() userToEdit: User;
    @Output() userInfo = new EventEmitter<User>

    buttonsDataToggleID: string = "userModal";

    constructor() { 

    }

    ngOnInit() {
        if (this.editMode) {
            this.buttonsDataToggleID = `userModal${this.userToEdit.uid}`
            this.onPupulateForm(this.userToEdit);
        } else {
            this.onPupulateForm(null);
        }
    }

    onSubmit() {
        this.userInfo.emit(this.dataToSend(this.userForm))
        this.userForm.reset();
    }

    onPupulateForm(user: User) {
        this.userForm = new FormGroup({
            'fullname': new FormControl(user ? user.fullname : null, Validators.required),
            'email': new FormControl(user ? user.email : null, [Validators.required, Validators.email]),
            'password': new FormControl(user ? user.password : null, [Validators.required, Validators.minLength(10)]),
            'role': new FormControl(user ? user.role : null, Validators.required),
            'createdAt': new FormControl(user ? user.createdAt : null, Validators.required)
        })
    }

    dataToSend(formValue: FormGroup) {
        const userInfo: User = {
            uid: this.userToEdit ? this.userToEdit.uid : Math.floor(Math.random() * 100001), 
            fullname: formValue.value.fullname,
            email: formValue.value.email, 
            password: formValue.value.password,
            createdAt: formValue.value.createdAt,
            role: formValue.value.role
        } 

        return userInfo;
    }

    onShowModal() {
        console.log(this.buttonsDataToggleID)
    }
}
Jojanyu15 commented 4 months ago

I'm getting the same issue and still can't figure it out what is going on