How do you use confirm dialogues in a custom Laravel Nova tool?
Asked Answered
A

3

21

Is it possible to use the built in Laravel Nova confirm dialogue in your own tool? All I would like to use is interact with it how Nova does itself.

The docs are quite light on the JS topic, as the only built in UI you seem to be able to work with is the toasted plugin: https://nova.laravel.com/docs/1.0/customization/frontend.html#javascript

Built in Laravel Nova confirm dialogue

Amimia answered 22/2, 2019 at 9:32 Comment(2)
For those not familiar with nova, can you clarify what you mean by "use the built in Laravel Nova confirm dialogue"? Are you talking about the dialogue/modal itself (html,css,js) or some functionality related to it?Boiled
He's talking about Nova's "modal" functionality seen in his screenshot.Rubio
I
10

You can use <modal> component whenever you want.

Here is how it work internally in Nova:

<template>
    <modal
        data-testid="confirm-action-modal"
        tabindex="-1"
        role="dialog"
        @modal-close="handleClose"
        class-whitelist="flatpickr-calendar"
    >
        <form
            autocomplete="off"
            @keydown="handleKeydown"
            @submit.prevent.stop="handleConfirm"
            class="bg-white rounded-lg shadow-lg overflow-hidden"
            :class="{
                'w-action-fields': action.fields.length > 0,
                'w-action': action.fields.length == 0,
            }"
        >
            <div>
                <heading :level="2" class="border-b border-40 py-8 px-8">{{ action.name }}</heading>

                <p v-if="action.fields.length == 0" class="text-80 px-8 my-8">
                    {{ __('Are you sure you want to run this action?') }}
                </p>

                <div v-else>
                    <!-- Validation Errors -->
                    <validation-errors :errors="errors" />

                    <!-- Action Fields -->
                    <div class="action" v-for="field in action.fields" :key="field.attribute">
                        <component
                            :is="'form-' + field.component"
                            :errors="errors"
                            :resource-name="resourceName"
                            :field="field"
                        />
                    </div>
                </div>
            </div>

            <div class="bg-30 px-6 py-3 flex">
                <div class="flex items-center ml-auto">
                    <button
                        dusk="cancel-action-button"
                        type="button"
                        @click.prevent="handleClose"
                        class="btn text-80 font-normal h-9 px-3 mr-3 btn-link"
                    >
                        {{ __('Cancel') }}
                    </button>

                    <button
                        ref="runButton"
                        dusk="confirm-action-button"
                        :disabled="working"
                        type="submit"
                        class="btn btn-default"
                        :class="{
                            'btn-primary': !action.destructive,
                            'btn-danger': action.destructive,
                        }"
                    >
                        <loader v-if="working" width="30"></loader>
                        <span v-else>{{ __('Run Action') }}</span>
                    </button>
                </div>
            </div>
        </form>
    </modal>
</template>

<script>
export default {
    props: {
        working: Boolean,
        resourceName: { type: String, required: true },
        action: { type: Object, required: true },
        selectedResources: { type: [Array, String], required: true },
        errors: { type: Object, required: true },
    },
    /**
     * Mount the component.
     */
    mounted() {
        // If the modal has inputs, let's highlight the first one, otherwise
        // let's highlight the submit button
        if (document.querySelectorAll('.modal input').length) {
            document.querySelectorAll('.modal input')[0].focus()
        } else {
            this.$refs.runButton.focus()
        }
    },
    methods: {
        /**
         * Stop propogation of input events unless it's for an escape or enter keypress
         */
        handleKeydown(e) {
            if (['Escape', 'Enter'].indexOf(e.key) !== -1) {
                return
            }
            e.stopPropagation()
        },
        /**
         * Execute the selected action.
         */
        handleConfirm() {
            this.$emit('confirm')
        },
        /**
         * Close the modal.
         */
        handleClose() {
            this.$emit('close')
        },
    },
}
</script>

Here is simplified example:

<modal>
    <form
            autocomplete="off"
            class="bg-white rounded-lg shadow-lg overflow-hidden"
    >
        <div>
            <heading :level="2" class="border-b border-40 py-8 px-8">test</heading>
            test
        </div>
        <div class="bg-30 px-6 py-3 flex">
            <div class="flex items-center ml-auto">
                <button
                        type="button"
                        class="btn text-80 font-normal h-9 px-3 mr-3 btn-link"
                >
                    {{ __('Cancel') }}
                </button>

                <button
                        ref="runButton"
                        type="submit"
                        class="btn-danger"
                >
                    <span>{{ __('Run Action') }}</span>
                </button>
            </div>
        </div>
    </form>
</modal>

Indetermination answered 27/2, 2019 at 5:20 Comment(3)
Sadly, both links now lead to a 404 and the Wayback Machine has no copies either...Blakney
@Kovah fixed, changed my github username sometime ago.Indetermination
Hi @Sanasol, I'm new to vueJs, can you help me, please, how can I use it? where put the code?Cothurnus
C
0
  • You need to create a new component in the same folder of Tool.vue
  • I'll attach the component I used here
  • Then in the "handleConfirm" method, you can add a Ajax call to API
  • You can add you logic in that API.
  • You can find the API file in path, ToolName/routes/api.php
/* CustomModal.vue */

<template>
   <modal tabindex="-1" role="dialog" @modal-close="handleClose">
       <form @keydown="handleKeydown" @submit.prevent.stop="handleConfirm" class="bg-white rounded-lg shadow-lg overflow-hidden w-action">
           <div>
               <heading :level="2" class="border-b border-40 py-8 px-8">Confirm action</heading>
               <p class="text-80 px-8 my-8"> Are you sure you want to perform this action? </p>
           </div>
           <div class="bg-30 px-6 py-3 flex">
               <div class="flex items-center ml-auto">
                   <button type="button" @click.prevent="handleClose" class="btn btn-link dim cursor-pointer text-80 ml-auto mr-6">
                       Cancel
                   </button>
                   <button :disabled="working" type="submit" class="btn btn-default btn-primary">
                       <loader v-if="working" width="30"></loader>
                       <span v-else>Confirm</span>
                   </button>
               </div>
           </div>
       </form>
   </modal>
</template>

<script>
export default {
 methods: {
   handleConfirm() {
     // Here you can add an ajax call to API and you can add your logic there.
   },
   handleClose() {
     // Logic to hide the component
   },
 },
}
</script>

For more detailed explanation : https://medium.com/vineeth-vijayan/how-to-use-confirm-dialogs-in-a-laravel-nova-tool-b16424ffee87

Collyer answered 5/7, 2020 at 9:3 Comment(0)
P
0

The only answer that worked well for me on Laravel Nova 4.x is below.

CustomConfirmModal.vue

<template>
    <Modal @modal-close="handleClose"
        :show="show"
        role="alertdialog"
        size="sm">
        <form
            @submit.prevent="handleConfirm"
            class="mx-auto bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden"
        >
            <slot/>
            <ModalFooter>
                <div class="ml-auto">
                    <LinkButton
                        type="button"
                        data-testid="cancel-button"
                        dusk="cancel-delete-button"
                        @click.prevent="handleClose"
                        class="mr-3"
                    >
                        {{ __('Cancel') }}
                    </LinkButton>

                    <LoadingButton
                        ref="confirmButton"
                        dusk="confirm-delete-button"
                        :processing="working"
                        :disabled="working"
                        component="DangerButton"
                        type="submit"
                    >
                        {{ confirmButtonText }}
                    </LoadingButton>
                </div>
            </ModalFooter>
        </form>
    </Modal>
</template>

<script setup>
import {ref, watchEffect} from 'vue'

const props = defineProps({
    confirmButtonText: {
        type: String,
        default: 'Delete'
    }
})
const emit = defineEmits(['close', 'confirm'])

const confirmButton = ref(null)
watchEffect(() => {
    // Only focus when mounted (e.g. if hidden through :show)
    if (confirmButton.value) {
        confirmButton.value.focus()
    }
})

const handleClose = () => {
    emit('close')
};

const handleConfirm = () => {
    emit('confirm')
};
</script>

Tool.vue or Card.vue

<template>
    <!-- Other code... -->
    <CustomConfirmModal
        :show="open_my_modal"
        confirmButtonText="Confirm"
        @close="closeModal"
        @confirm="confirmModal">
        <ModalHeader>Confirm</ModalHeader>
        <ModalContent>
            <p class="leading-normal">
                Do you really want to perform this action?
            </p>
        </ModalContent>
    </CustomConfirmModal>

    <button @click="openModal">Test My Modal</button>
</template>

<script>
    //Other JS Code...
    import CustomConfirmModal from "./CustomConfirmModal";
    export default {
        components: {
            CustomConfirmModal
        },
        data() {
            return {
                //Other variables...
                open_my_modal: false
            }
        },
        methods: {
            //Other methods code...
            openModal() {
                this.open_my_modal = true;
            },
            confirmModal() {
                this.open_my_modal = false;
            },
            closeModal() {
                this.open_my_modal = false;
            }
        }
        // Other JS code...
    }
</script>

The output you'll see would be similar to the below dialog. enter image description here

Reference link

Punctate answered 27/6, 2024 at 14:14 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.