Promise based Dialog in Vue 2

Dialogs visually exist “outside” application, and because of it, never really felt right for me to include them in places where they don’t belong to. HTML regarding Dialogs is often placed in the root of the application or in the components where they …


This content originally appeared on DEV Community and was authored by Adam Kalinowski

Dialogs visually exist "outside" application, and because of it, never really felt right for me to include them in places where they don't belong to. HTML regarding Dialogs is often placed in the root of the application or in the components where they are called from, and then, usually by portals, transferred to the top. Logic, which is controlling which dialog should pop up and when, is also, either in store or component, or maybe have its own service. Sometimes logic meant to control dialogs is lacking in features, and then, oops, we cannot open dialog inside another dialog. Too bad if we need it.

I feel like we can solve all the issues with simply handling dialogs as a function. We want dialog? Let's call it, and as a parameter put the component we want to display. We can wrap it in a promise, so we know exactly when the dialog is closed and with what result, and then make some calls based on that.

To visualize how I imagine working with that I made snippet below:

const success = await openDialog(ConfirmDialog)
if (success) {
  this.fetchData()
}

The benefit of doing all the logic regarding dialogs by ourselves is that we have full control over this, we can add new features based on our needs, and make our dialogs look however we want. So, let's build it.

First, we need to create Dialog Wrapper component. Its purpose is to provide basic styles and some logic for closing the dialog.

<template>
  <div class="dialog-container">
    <span class="dialog-mask" @click="$emit('close')"></span>
    <component :is="dialogComponent" @close="response => $emit('close', response)"
               v-bind="props"/>
  </div>
</template>
<script>
export default {
  name: 'DialogWrapper',
  props: ['dialogComponent', 'props']
}
</script>
<style>
.dialog-container {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1001;
}
.dialog-mask {
  position: fixed;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.4);
}
</style>

You can change styles so it fits you. You can also add additional logic, we can add animations and other features, but I wanted to keep it simple. You will be getting two props, dialogComponent and props (confusing, I know).

  • dialogComponent is Vue component which will be rendered inside
  • props are props passed to dialogComponent

You close dialog by emitting event close, and if you want to pass a value which will be used when resolving a promise - you pass data with the event, e.g. $emit('close', 'success!').

Now let's make a function.

export function openDialog (dialogComponent, props) {
  return new Promise((resolve) => {
    const Wrapper = Vue.extend(DialogWrapper)
    const dialog = new Wrapper({
      propsData: {
        dialogComponent,
        props,
      },
      router, // optional, instance of vue router
      store, // optional, instance of vuex store
    }).$mount()
    document.body.appendChild(dialog.$el);

    dialog.$on('close', function (value) {
      dialog.$destroy();
      dialog.$el.remove();
      resolve(value)
    })
  })
}

It will create a new Vue instance and append it to document.body. It will use DialogWrapper as main component, and will pass function parameters as props by using propsData property. It will also listen for close event to know where to destroy itself.

It's important to add router and store property when initializing component, if you're using it, because otherwise your components will have no access to $store and $router.

So we have our dialog function working, but I cut a lot of code I'm using for conveniance of this article, and leave only the core logic. It's good idea to create another component - let's call it DialogLayout, which will create actual white box with some padding. You can, if you want, put some more effort in that; for example, adding dialog title or close button.

<template>
  <div class="dialog-content">
    <slot></slot>
  </div>
</template>

<style scoped>
.dialog-content {
  width: 60%;
  position: relative;
  margin: 100px auto;
  padding: 20px;
  background-color: #fff;
  z-index: 20;
}
</style>

Now, we can move into testing part of the article.

Let's create example component which we will later pass as a openDialog parameter.

<template>
  <DialogLayout>
    <button @click="$emit('close', 'wow! success')">Close dialog</button>
  </DialogLayout>
</template>

It has button which will close the dialog with resolved value 'wow! success. It also uses DialogLayout for some basic styling.

Somewhere in our application we can call our function:

    async onBtnClick () {
      const result = await openDialog(DialogExample)
      // dialog is now closed
      console.log(result) // 'wow! success'
    }

Although it requires some initial configuration, payback is huge. I'm using it for years now and it fits my needs perfectly. It's also easy to extend with additional features.

It's important to note, that this dialog will not be animated. Animation can be added quite easily, but it's beyond scope of this article.

Thanks a lot for reading, and in case of any questions, please write comment or send me an email - iam.adam.kalinowski@gmail.com. Have a nice day!


This content originally appeared on DEV Community and was authored by Adam Kalinowski


Print Share Comment Cite Upload Translate Updates
APA

Adam Kalinowski | Sciencx (2021-05-18T20:30:40+00:00) Promise based Dialog in Vue 2. Retrieved from https://www.scien.cx/2021/05/18/promise-based-dialog-in-vue-2/

MLA
" » Promise based Dialog in Vue 2." Adam Kalinowski | Sciencx - Tuesday May 18, 2021, https://www.scien.cx/2021/05/18/promise-based-dialog-in-vue-2/
HARVARD
Adam Kalinowski | Sciencx Tuesday May 18, 2021 » Promise based Dialog in Vue 2., viewed ,<https://www.scien.cx/2021/05/18/promise-based-dialog-in-vue-2/>
VANCOUVER
Adam Kalinowski | Sciencx - » Promise based Dialog in Vue 2. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/05/18/promise-based-dialog-in-vue-2/
CHICAGO
" » Promise based Dialog in Vue 2." Adam Kalinowski | Sciencx - Accessed . https://www.scien.cx/2021/05/18/promise-based-dialog-in-vue-2/
IEEE
" » Promise based Dialog in Vue 2." Adam Kalinowski | Sciencx [Online]. Available: https://www.scien.cx/2021/05/18/promise-based-dialog-in-vue-2/. [Accessed: ]
rf:citation
» Promise based Dialog in Vue 2 | Adam Kalinowski | Sciencx | https://www.scien.cx/2021/05/18/promise-based-dialog-in-vue-2/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.