Blog / Afficher un modal avec VueJS et son composant Teleport

Image used for article Afficher un modal avec VueJS et son composant Teleport

Afficher un modal avec VueJS et son composant Teleport




TL;DR: Comment implémenter rapidement un modal avec VueJS en utilisant son composant intégré Teleport.




Vous trouverez un lien vers une Demo CodeSandbox ou le code source via ce Github Repository. Découvrez en plus sur Capsules ou X.




Utiliser un modal peut sembler évident sur un site web, sa mise en place peut parfois s'avérer complexe. Pour simplifier cette tâche, le framework Vue a mis en place son composant intégré <Teleport>. Celui-ci nous permet de "téléporter" une partie du modèle d'un composant dans un nœud du DOM existant en dehors de la hiérarchie du DOM de ce composant.



Déterminons maintenant l'emplacement de ce nœud externe en attribuant l'id capsules à Welcome.vue.



/resources/js/pages/Welcome.vue


<script setup>

import logotype from '/public/assets/capsules-logotype-red-blue-home.svg';

</script>

<template>

    <div id="capsules" class="w-full min-h-screen flex flex-col font-sans text-primary-black">

        <div class="grow mx-8 lg:mx-auto max-w-screen-lg overflow-auto flex flex-col items-center justify-center text-center">

            <img class="w-24 h-24" v-bind:src="logotype">

            <h1 class="mt-4 text-6xl font-bold select-none header-mode" v-text="'Capsules Codes'" />

        </div>

    </div>

</template>




Ce composant Vue est en réalité une page InertiaJS appelée lors de l’appel à la route principale indiquée dans le fichier web.php



routes/web.php


<?php

use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

Route::get( '/', fn() => Inertia::render( 'Welcome' ) );







Avec l'id capsules maintenant attribué, le composant Modal.vue va pouvoir utiliser le composant intégré <Teleport> sur cet élément.



/resources/js/components/Modal.vue


<script setup>

import { ref, onMounted } from 'vue';

const props = defineProps( { open : { type : Boolean, default : false } } );
const emits = defineEmits( [ 'toggle' ] );

const ready = ref( false );

function toggle()
{
    emits( 'toggle' );
}

onMounted( () => ready.value = true );

</script>

<template>

    <Teleport to="#capsules" v-if="ready">

        <Transition enter-active-class="duration-500 ease-in-out" enter-from-class="opacity-0" enter-to-class="opacity-100" leave-active-class="duration-500 ease-in" leave-from-class="opacity-100" leave-to-class="opacity-0">

            <div v-if="props.open" class="fixed w-full h-full flex items-center justify-center backdrop-blur-[1px] bg-primary-white bg-opacity-50" v-on:click="toggle()">

                <div class="relative m-16 p-2 rounded-xl flex flex-wrap items-center justify-center text-xs bg-white whitespace-pre shadow-2xl shadow-black/10" v-on:click.stop>

                    <slot />

                </div>

            </div>

        </Transition>

    </Teleport>

</template>




La variable ready permet de charger le composant afin qu'il puisse déclencher sa Transition de la manière la plus fluide possible.


L'événement v-on:click.stop, quant à lui, empêche toute propagation éventuelle du clic à d'autres éléments que le modal lui-même.


Le composant intégré <Transition> peut être utilisé pour appliquer des animations d'entrée et de sortie sur des éléments ou des composants qui lui sont transmis via son slot par défaut. Dans ce cas, il s'agit d'une transition en douceur de l'opacité.



La configuration du modal étant achevée, il est désormais temps de créer un composant qui permettra d'afficher ce modal. Dans ce cas-ci : un bouton.



resources/js/components/Button.vue


<script setup>

import { ref, watch } from 'vue';

import Modal from '/resources/js/components/Modal.vue';

import logotype from '/public/assets/capsules-logotype-red-blue-home.svg';

const button = ref();
const isModalOpen = ref( false );

watch( () => isModalOpen.value, () => isModalOpen.value ? window.addEventListener( 'click', clickOutside ) : window.removeEventListener( 'click', clickOutside ) );

function clickOutside( event )
{
    if( event.target === button.value || !event.composedPath().includes( button.value ) ) isModalOpen.value = false;
}

</script>

<template>

    <div ref="button" class="m-4">

        <button class="px-4 py-2 text-sm rounded-md border border-primary-black hover:border-primary-red hover:text-primary-red transition-all" v-on:click="isModalOpen = true" v-bind:class="{ 'opacity-25' : isModalOpen }" v-bind:disabled="isModalOpen" v-text="'Open Modal'" />

        <Modal v-bind:open="isModalOpen" v-on:toggle="isModalOpen = false">

            <div class="p-8 flex flex-row space-x-4 rounded-lg">

                <img class="w-12 h-12 select-none" v-bind:src="logotype">

                <div class="font-mono flex items-center">

                    <h2 class="text-lg align-middle" v-text="'A wild MODAL appeared!'"/>

                </div>

            </div>

        </Modal>

    </div>

</template>




Une variable button est créée et associée à la div parente du Modal, ce qui permet de surveiller tout clic effectué en dehors de ce composant. Un watcher est utilisé pour observer la variable isModalOpen afin de déterminer si la fonction clickOutside doit être déclenchée lors d'un clic sur l'écran. Si le Modal est ouvert, la fonction est activée, sinon, elle ne l'est pas.



Le composant Button.vue peut être ajouté à la page Welcome.vue



resources/js/pages/Welcome.vue


<script setup>

import logotype from '/public/assets/capsules-logotype-red-blue-home.svg';

import Button from '~/components/Button.vue';

</script>

<template>

    <div id="capsules" class="w-full min-h-screen flex flex-col font-sans text-primary-black">

        <div class="grow mx-8 lg:mx-auto max-w-screen-lg overflow-auto flex flex-col items-center justify-center text-center">

            <img class="w-24 h-24" v-bind:src="logotype">

            <h1 class="mt-4 text-6xl font-bold select-none header-mode" v-text="'Capsules Codes'" />

            <Button class="pt-8" />

        </div>

    </div>

</template>







Le modal s’affiche lors du clic sur le bouton. Il est maintenant possible de personnaliser le Modal à sa guise : sa position, ses dimensions, ses actions. Le composant de Reaction implémenté sur le Blog Capsules Codes est aussi le fruit de cette méthode.







Ravi d’avoir pu aider.

v1.2.1

Icône XIcône Github