Tips for managing modal closure when the InertiaJS form succeeds?

Hello everyone! Currently, I'm involved in a Laravel project where I am using laravel/breeze VueJS with Inertia. The login functionality is implemented using a bootstrap modal component. While validation and authentication are working smoothly, the only issue I'm facing is that after successful authentication, the modal closes but the backdrop remains visible.

I've noticed that the form onSuccess does not trigger any emit function. Interestingly, when I used console.log, it worked perfectly fine.

Below you can find the code snippets for reference...

Header.vue

<script setup>
import { ref } from 'vue';
import Modal from '@/Components/Modal.vue'

let thisModal = ref(null)

function showModal() {
    thisModal.value.show()
}

function closeModal() {
    thisModal.value.close()
}
</script>

<template>
    <header class="shadow-sm">
        <Modal v-if="! $page.props.auth.user" ref="thisModal">
            <template #body>
                <Login @closeParent="closeModal" @showParent="showModal" />
                <Register />
            </template>
        </Modal>
    </header>
</template>

Modal.vue

<script setup>
import { onMounted, ref } from 'vue'
import { Modal } from 'bootstrap'

defineProps({
    title: String
})

let modalEle = ref(null)
let thisModalObj = null

onMounted(() => {
    thisModalObj = new Modal(modalEle.value)
})

function _show() {
    thisModalObj.show()
}

function _close() {
    thisModalObj.hide()
}

defineExpose({ show: _show, close: _close })
</script>

<template>
    <div class="modal fade" tabindex="-1" role="dialog" ref="modalEle">
        <div class="modal-dialog modal-dialog-centered" role="document">
            <div class="modal-content">
                <div class="modal-header bg-secondary">
                    <slot name="header" />
                </div>
                <div class="modal-body tab-content py-4">
                    <slot name="body" />
                </div>
                <slot name="footer" />
            </div>
        </div>
    </div>
</template>

Login.vue (Form location)

<script setup>
import { Link, useForm } from '@inertiajs/vue3'
import Checkbox from '@/Components/Checkbox.vue'
import InputError from '@/Components/InputError.vue'
import InputLabel from '@/Components/InputLabel.vue'
import PrimaryButton from '@/Components/PrimaryButton.vue'
import TextInput from '@/Components/TextInput.vue'
import PasswordToggle from '@/Components/PasswordToggle.vue'

defineProps({
    canResetPassword: {
        type: Boolean,
    },
    status: {
        type: String,
    },
})

const form = useForm({
    email: '',
    password: '',
    remember: false,
})

const emit = defineEmits()

const submit = () => {
    form.post(route('login'), {
        onFinish: () => form.reset('password'),
        // onBefore hides the modal
        onBefore: () => emit('closeParent'),
        // onError shows the modal
        onError: () => emit('showParent'),
        // Use onSuccess instead
        onSuccess: () => emit('closeParent') 
    })
}
</script>

<template>
    <form @submit.prevent="submit" id="login-tab" class="tab-pane fade show active" autocomplete="off" novalidate>
        <!-- Form fields here -->
    </form>
</template>

The above code works such that the modal is hidden upon clicking the Sign in button and will reappear if there is an error like validation failure or incorrect credentials. However, I want it to close only when authentication is successful. The problem may lie in the fact that the onSuccess method receives an Inertia response as shown in the controller below:

AuthenticatedSessionController.php

/**
 * Handle an incoming authentication request.
 */
 public function store(LoginRequest $request) // : RedirectResponse
 {
     $request->authenticate();

     $request->session()->regenerate();

     return redirect()->intended(RouteServiceProvider::HOME);
  }

Also, I'd like to mention that the modal code was shared by @OJay in response to a query posted here.

Answer №1

After extensive testing, I still haven't found the most effective way to address this issue. Instead of using an API, I have opted to stick with Inertia response/redirects. To fix the problem of the modal backdrop persisting after a successful login redirect, I have chosen to manually remove the modal backdrop on the modal parent update event.

  1. If the login is successful, a redirect response is triggered.

    return redirect()->intended(RouteServiceProvider::HOME);

This results in the page being redirected to the home page without properly closing the modal form, causing the modal backdrop to cover the entire page.

  1. To address this issue, I have implemented code that checks the body tag for modal classes and scans the entire document for

    <div class="modal-backdrop fade show"></div>
    , along with a body style attribute of
    style="margin-bottom: 8px; overflow: hidden; padding-right: 0px;"

  2. When the modal is opened, Bootstrap updates the body tag class to

    <body class="handheld-toolbar-enabled modal-open">
    along with the style attribute changing to
    style="margin-bottom: 8px; overflow: hidden; padding-right: 0px"
    . It then adds a new element
    <div class="modal-backdrop fade show"></div>

  3. Taking note of these changes, I have devised a solution to remove and update these elements, which is activated by the modal parent's onUpdated event. The code is outlined below

<script setup>
import { ref, onUpdated } from 'vue'
// Get the document's body tag
const body = ref(document.body)

// Function to check and update body tag
const checkAndModifyBody = () => {
    const modalOpenClass = 'modal-open'
    const expectedStyle = 'margin-bottom: 8px;'
    const modalBackdropSelector = 'div.modal-backdrop.fade.show'

    if (body.value.classList.contains(modalOpenClass)) {
        body.value.classList.remove(modalOpenClass)
    }

    if (body.value.style.cssText !== expectedStyle) {
        body.value.style.cssText = 'margin-bottom: 8px;'
    }

    const modalBackdrop = body.value.querySelector(modalBackdropSelector)
    if (modalBackdrop) {
        modalBackdrop.remove()
    }
}

// Watch for changes and run the checkAndModifyBody function
watch(body, () => {
    checkAndModifyBody()
});

// Initial check
onUpdated(() => {
    checkAndModifyBody()
})

This method may not be the most elegant vue approach to solving the issue.

Thank you all for your input, and if anyone has a more efficient solution, please share it with me.

Cheers!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Toggle the visibility of an element by clicking a button

I have a table structured as follows: <tr> <td class="title">Title</td> <td class="body">Body</td> <td class="any">Any text</td> </tr> <tr> <td class="title">Title</td> ...

Utilizing PHP to create an interactive website

As a novice PHP developer, I took to Google in search of tutorials on creating dynamic PHP websites. What I found was that many tutorials used the $_GET variable to manipulate URLs and make them appear like this: example.com/?page=home example.com/?page= ...

Can Vue.js be paired with pure Node.js as a backend without relying on Express?

After successfully building a project with both vue js and node js, specifically with express, I'm curious if it's feasible to solely utilize node js without frameworks like express. ...

Is there a way to incorporate the ACE code editor into a Vue project without relying on its built-in Vue

Just starting out with Vue and I'm looking to incorporate the ace code editor (this package) into my project. However, I want to avoid using the vue2-component & vue3-component versions for learning purposes. How can I achieve this? What's t ...

Is there a way to optimize app speed in Angular2 by importing CommonModule and RouterModule in a centralized location?

I find myself constantly importing these two modules in almost every component: import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; Is there a way to import them only once in the global app. ...

The function `req.on('end', callback)` in Node.js is not functioning as expected

I am currently working on building a proxy using nodejs. The syntax I am using for sending and receiving https requests and responses works well, but in my project, I have noticed that the response is sometimes larger than expected. This results in req.on( ...

Troubleshooting the issue of React forms hook not functioning properly with Material UI Select input

How the Textfield below should load: How it actually loads: My Select component, created using Material UI and React form hook, is not loading the default value as expected. The component should start with a pre-selected value, which is provided in the c ...

Using angular.js variables in the script tag: A step-by-step guide

Currently, I am working on a project that involves displaying elements on a view using Angular.js and attempting to connect these DOM elements using jQuery connections. While the elements are being displayed correctly, I am encountering an issue when tryin ...

Use leaflet.js in next js to conceal the remainder of the map surrounding the country

I'm currently facing an issue and would appreciate some assistance. My objective is to display only the map of Cameroon while hiding the other maps. I am utilizing Leaflet in conjunction with Next.js to showcase the map. I came across a helpful page R ...

What is the best way to hide the next/previous tabs once the jQuery dataTable has been set up using jSON data

Setting up a jQuery table using JSON data. Despite knowing that there will only be one row, the next/previous tabs are still displayed after the table. Is there a way to remove them? Here is the code for the table: table = $("#retrievedTable").dataTabl ...

The issue of React Js's inline style malfunctioning when using a loop condition

Having some trouble with setting different backgrounds for items in a loop in React JS. I attempted to use inline styles to make it dynamic, but no luck so far. Any tips or solutions? { main.map((item, index) => ( <a key={index} href=&apo ...

Problems with navigation, not functioning properly due to issues with Pulled functions

I am still getting the hang of things and struggling with the terminology, so please bear with me as I try to explain my issue. Currently, I am working on a project in react native where I have two files - Header.js and footer.js. I have successfully impo ...

Troubleshooting issues with cross-domain jQuery ajax requests

Struggling with this code and unable to make it work. This call consistently returns a "Failed to load resource: the server responded with a status of 401 (Unauthorized)" error message. $('#btnZendesk').click(function () { $.ajax({ ...

Exploring Parquet Files with Node.js

Looking for a solution to read parquet files using NodeJS. Anyone have any suggestions? I attempted to use node-parquet but found it difficult to install and it struggled with reading numerical data types. I also explored parquetjs, however, it can only ...

What is the best method for storing a Google Analytics object in a $_SESSION variable when transitioning between PHP scripts to avoid encountering a non-object error

Currently in the process of developing a web application that integrates with the Google Analytics API, however encountering challenges when it comes to implementation. The user is given the option to choose their profile from three interconnected drop-do ...

Is the xmlhttprequest timeout/abort feature not functioning as anticipated?

Check out this snippet of my AJAX function: /** * This function initiates an AJAX request * * @param url The URL to call (located in the /ajax/ directory) * @param data The data to send (will be serialized with JSON) * @param callback The fu ...

Is there a way to retrieve two separate route details using jQuery simultaneously?

Clicking the checkbox should display the Full Name: input type="text" id="demonum" size="05"> <button type="button" onclick="load_doc()">click</button><br><br> <input type="checkbox" id ="check" > The r ...

Sending Parsed Information to Callback for Flexible Use

Is there a way to pass the value of coins, or even better, currency to my callback function so I can freely use the parsed JSON data in other functions? function fetchJSON(path, callback) { var jsonReq = new XMLHttpRequest(); jsonReq.onreadystatechang ...

What is the best approach for transmitting data to the post method of Node.js using AJAX/jQuery?

A UDP server I have is set up to respond with a different message each time it receives a message. When I hard code the message into the variable "message," everything works fine. However, I want the client to be able to manually type in a message, and t ...