Search code examples
javascriptvue.jsmodal-dialogquasar-frameworkquasar

Interrupt Quasar QDialog closing on Escape key, given a certain condition


I use a Quasar dialog for showing some content. I want the users to be able to close the dialog when clicking the Escape button, which works fine. However, if the user made some edits in the dialog and hits the Escape key, I would like to show another dialog, asking whether the user really wants to discard the changes to prevent accidental data loss.

The problem is that I can either completely disable the Escape key like this:

<template>
  <q-dialog
    v-model="showDialog"
    no-backdrop-dismiss
    no-esc-dismiss    
  >
</template>

but then the user won't be able to close the dialog with the escape key, even if they didn't perform any changes or I can allow the escape key and try to intercept the event:

<template>
  <q-dialog
    v-model="showDialog"
    no-backdrop-dismiss
    @escape-key="dismissDialog"
  >
</template>

<script setup lang="ts">
function dismissDialog() {
  if (unsavedChanges) {
    Dialog.create({
      title: 'Unsaved changes',
      message: 'You have unsaved changes. Do you want to discard them?',
      ok: 'Discard changes',
      cancel: true,
    })
      .onCancel(() => {
        return;
      })
      .onOk(() => {
        emit('dismissDialog');
      });
  } else {
    emit('dismissDialog');
  }
}
</script>

The problem is that in the second scenario I can easily detect that there are unsaved changes, but the moment I leave dismissDialog, the dialog will be closed anyway and I don't seem to have any possibility to interrupt closing the dialog.

I've also checked the @before-hide method, but the event I'm getting there is always undefined so it seems like I have no way to intercept the escape key event and prevent the dialog closing, unless I completely disable the escape key.

Does anyone have an idea how to do this?


Solution

  • You can listen to @update:model-value and always set dialog to true if there are unsaved changes so it doesn't close.

    <q-dialog v-model="firstDialog" @update:model-value="checkUnsavedChanges">
    ...
    </q-dialog>
    

    Then you can toggle another dialog on top of the first one, with another v-model.

    function checkUnsavedChanges() {
      if (unsavedChanges.value) {
        firstDialog.value = true;
        secondDialog.value = true;
        return;
      }
      firstDialog.value = false;
    }
    

    Then from the second dialog you can chose which dialog to close.

    <q-dialog v-model="secondDialog" persistent>
        <q-card>
          <q-card-section> are you sure? </q-card-section>
          <q-card-actions>
            <q-btn label="close both" @click="closeAll" />
            <q-btn v-close-popup label="close only this" />
          </q-card-actions>
        </q-card>
      </q-dialog>
    
    function closeAll() {
      firstDialog.value = false;
      secondDialog.value = false;
      unsavedChanges.value = false;
    }
    

    Check the Playground