vue-final / vue-final-modal

🍕Vue Final Modal is a tiny, renderless, mobile-friendly, feature-rich modal component for Vue.js.
https://vue-final-modal.org
MIT License
905 stars 99 forks source link

Dialog result as a promise #193

Closed kkuegler closed 2 years ago

kkuegler commented 2 years ago

Is your feature request related to a problem? Please describe.

Being able to show dynamic modals is very nice. I'd like to be able to open a dialog and wait until the user used one of the buttons and get the "result" of the dialog back. This could be a boolean (e.g. for ok/cancel), but also a more complex result object built in the dialog.

Technically this is possible with the 'on' callbacks in vfm.show(), but having multiple callbacks (for each button) makes it hard to write imperative code that works with the dialog result. I often find Promises easier to work with and to understand than callbacks.

Describe the solution you'd like

I would like to have a method that shows the dialog and returns a promise that is resolved when one of the dialog buttons is clicked. The promise value should contain information about which button was used to close the dialog, as well as an optional 'result' object, e.g. containing the form data entered in the dialog. This e.g. makes using an ok/cancel dialog in an if statement very easy.

I'd like to do something like this:

if ((await $vfm.showAndAwait({component: YesNoDialog})).result === true) {
  // so something
}

I've built a prototype in a sandbox to show what kind of functionality I imagine.

The basic API is

const answer = await getFromModal(MyDialogComponent); //shows dialog and waits for user

answer has an action property naming the button (e.g. 'ok' or 'cancel'). It also has a result property for additional data that is the 'result' payload of the dialog.

See the showDialog and confirmDialog functions in App.vue for some practical examples.

For this approach to work, the dialogs itself need to cooperate as well. They emit special result events containing the used button and optionally a dialog result. These events are then translated into the promise values.

The file src/prom/dialog-promise.ts contains the implementation for this. It has closeWithResult/closeWithoutResult methods, that can emit the proper event(s).

See ConfirmDialog.vue or ExampleDialog.vue how this could look in the Options API. Basically each dialog button does something like this.closeWithResult("ok-button", this.additionalDialogResultData).

This is just an API draft to get the idea across. All the details are up for debate. For the Composition API, the API style would probably need to be adapted as well.

Describe alternatives you've considered

Something like this could also exist as a separate add-on library for vue-final-modal. But I wanted to see if you find it helpful enough to include some API like this in vue-final-modal itself.

hunterliu1003 commented 2 years ago

Hi @kkuegler,

I've built a prototype in a sandbox to show what kind of functionality I imagine.

I read your code in the prototype. Thanks the feedback and letting me know your idea. I prefer to keep $vfm.show return a promise for the modal is opened, because creating a showAndAwait function is really easy. Just like you implement the getFromModal function in src/prom/dialog-promise.ts.

Another reason is because vue-final-modal is a styleless component. That means I don't know how many buttons will be put in the vue-final-modal (it depends on the developer), so I think providing a built-in method in $vfm might not a good idea.

I wanna let the core of vue-final-modal as flexible as possible.

Something like this could also exist as a separate add-on library for vue-final-modal. But I wanted to see if you find it helpful enough to include some API like this in vue-final-modal itself.

I already have a plan to provide some ready-to-use components like VConfirm, VBottomSheet, VFullscreenModal in version 3.5.x. Maybe we can also provide a function showing modal and return Promise that including the action key in response.

import { useConfirm } from 'vue-final-modal'

const { show } = useConfirm()

(async function() {
  const { action } = await show()
  if (action === 'confirm') {
    // user click confirm button
  } else if (action === 'cancel') {
    // user click cancel button
  }
})()
kkuegler commented 2 years ago

Hey @hunterliu1003

thanks for looking into this :) I appreciate your feedback regarding promise support in the core library. I think closing this ticket makes sense. Maybe I'll look into making a small add-on library in the future, after using and refining this idea some more. We'll see :)

It will be interesting to see, how ready-to-use components will work in detail. Keep up the good work!