DEV Community

GaurangDhorda
GaurangDhorda

Posted on

Reusable Dialog component using Vue3 composition api

Creating reusable dialog component using vue3 composition api. Open and Close multiple dialog stack from within.

You can support me

Alt Text

What is composition api.

Composition api is vue3 feature, where we can simplify our component logic by moving to another file and used from multiple component. Meaning If you are aware of React Contenxt api, same we will maintain context of app and share common context between multiple components and manipulate data from only single source.

I am assuming You are using Vue/Cli and already created your app using Vue/Cli with vue3 setup.

first of all lets create our main part of dialog composition logic. Create new file under Api/toggleModel.js and then add below code under this file.

import { reactive, readonly } from "vue";

const modal = reactive({
  role: []
});

export default function useToggleModal() {
  const toggleModel = (role = "") => {
    modal.role.pop();
  };

  const openModal = (role = "") => {
    modal.role.push({ type: role, isOpen: true });
  };

  const hasRole = (role = "") => {
    if (role === "") return false;
    const findRole = modal.role.find(currentRole =>
      currentRole.type === "" ? null : currentRole.type === role
    );
    if (findRole === undefined) return false;

    return findRole.type === role && findRole.isOpen === true ? true : false;
  };

  return {
    state: readonly(modal),
    toggleModel,
    openModal,
    hasRole
  };
}

Enter fullscreen mode Exit fullscreen mode

[Step : 1] first we import bellow composition api function.

  • import { reactive, readonly } from "vue";

[Step : 2] then we create our gloabl state modal and makes state reactive(), so that each time we change value of state it will update state to component too, meaning component will get new state whenever this global state object is changed.

  • const modal = reactive({ role: [] });

[Step : 3], in step 3 we created one function useToggleModal() and inside all logic of modal state handling lives in. lets understand it step by step.

inside, useToggleModal() function we declared three inner function which are toggleModel, hasRole, openModal, as name suggests you can get hows this three functions working all togather.

hasRole(),
as name suggests, hasRole() function is used to check wether given dialog is open or not, based on this function return value, inside our dom we show dialog component using v-if. All user needs to have is pass dialog name to this hasRole() function, and this function returns true, if given role name is found open, or returns fasle if given role name is not found. here we check global state modal.role is available or not.

openModal(),
as name suggets, openModal() function is requires when ever user wants to open dialog onButton click event from any component, inside this function user has to pass one dialog role name, and same role is checked by hasRole() function, if Both role name found same dialog opens, otherwise dialog is in close state.

So, inside openModal() we pass role state, whatever user wants to pass in, the same role name is also passed on inside hasRole() too!.

toggalModel(),
as name suggets, currently opened dialog is closed when ever use clicks on right side close button.

and at last we return all this state function so that any component will import it and use it and component will change state. remember this state is global, one change from any component will affect other component if component is using state from this global function.

Now we create our main dialog component.

create new component file inside components/Dialog.vue, and add below code. we are not giving css right now..

<template>
  <h1> Dialog </h1>
  <div id="outSideDiv" @click.self="clickDiv"
    class="fixed modal-backdrop overflow-x-hidden overflow-y-auto inset-0 z-50  flex justify-center items-start mt-8 items-center">
    <div ref="divRef" class="relative w-auto  mx-auto max-w-sm max-w-2xl ">
      <div class="bg-white  w-full  rounded shadow-2xl ">
        <button @click="toggleModel"  class="fixed top-0 right-0">
              <svg
                width="20"
                height="20"
                class="text-red-900 bg-white hover:bg-gray-600 transition rounded"
                xmlns="http://www.w3.org/2000/svg"
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor"
              >
                <path
                  stroke-linecap="round"
                  stroke-linejoin="round"
                  stroke-width="2"
                  d="M6 18L18 6M6 6l12 12"
                />
              </svg>
            </button>

        <slot />
      </div>
    </div>
  </div>
</template>

<script>
  import { ref } from "vue";
  import useToggleModal from "../API/toggleModel";

  export default {
    name: 'Dialog',
    setup(){
      const { state, toggleModel } = useToggleModal();

      function clickDiv (){
        console.log('click')
      }
      return {
        clickDiv,
        toggleModel
      }
    }
  }
</script>
Enter fullscreen mode Exit fullscreen mode

we will first import our composition api inside script section of dialog component like this..

import useToggleModal from "../API/toggleModel";

and then, inside setup() function we get all returns function from useToggleModal.

const { state, toggleModel } = useToggleModal();

inside, template onButton click we directly using toggleModel function to close currently opend dialog.

Inside this dialog we used <slot /> , slot here is used to render user defined content inside this dialog component, so that user is free to decide what to do with this dialog.

now, lets see how we can use this dialog component inside our main app.js component...

<template>
  <div id="app">
    <button @click="onToggleModal('contentOne')"> Open Dialog </button>
    <Dialog v-if="hasRole('contentOne')">
      <ContentOne />
    </Dialog>
  </div>
</template>

<script>
  import Dialog from './components/Dialog';
  import ContentOne from './components/ContentOne';
  import useToggleModal from "./API/toggleModel";

  export default {
  name: 'App',
  setup(){
    const { openModal, hasRole } = useToggleModal();
    function onToggleModal(role) {
      openModal(role);
    }
    return{
      hasRole,
      openModal,
      onToggleModal
    }
  },
  components: {
    Dialog,
    ContentOne
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

here, inside app.js component also we will import our composition api function useToggleModal() and getting returned function inside setup() function.. like openModal, hasRole..

Then inside template of app.js component, check button of opendialog, and onClikc of this button, we pass unique name to openModal function, and same name is checked by dialog component using v-if, if given name hasRole() is opened by user or not.

First time when application loads, hasRole() functions gets called and no role found open, so dialog is closed default, after user clicks on openDialog button then both have same role name and given dialog component will open with their own template as we have used inside dialog component. like ...

so, ContentOne is another component and is being rendered inside our dialog component.

Now, lets see whats inside contentOne component and we also open new dialog from inside contentOne component, so that second dialog will open inside first dialog and dialog stack will loaded inside this dialog..

<template>
  <h3>Content One </h3>
  <button @click="onToggleModal('contentTwo')">Open Second </button>
  <Dialog v-if="hasRole('contentTwo')">
    <h3> Second Content </h3>
  </Dialog>
</template>

<script>
  import Dialog from './Dialog';
  import useToggleModal from "../API/toggleModel";

  export default{
    name: 'ContentOne',
    setup(){
        const { openModal, hasRole } = useToggleModal();
        function onToggleModal(role){
          openModal(role);
        }
        return {
          openModal,
          hasRole,
          onToggleModal
        }
    },
    components: {
      Dialog
    }
  }
</script>
Enter fullscreen mode Exit fullscreen mode

same as app.js component we will follow same structure, inside ContentOne component, just we open dialog with other name inside contentOne component and same has been checked by role inside hasRole()..

complete working example found inside below stackblitz link..

You can support me

Alt Text

Top comments (0)