Created
December 22, 2022 16:34
-
-
Save IlCallo/107c901b45ac8a470d9b06b7ec7b46ef to your computer and use it in GitHub Desktop.
Quasar Dialog helpers, which differentiate between create and edit mode
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export interface MyEntity { | |
id?: number; | |
title: string; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<q-dialog ref="dialogRef" @hide="onDialogHide"> | |
<q-card> | |
<!-- ... your content --> | |
<q-card-section> | |
{{ myEntity }} | |
</q-card-section> | |
<q-card-actions align="evenly"> | |
<q-btn v-close-popup label="Cancel" color="primary" /> | |
<q-btn label="Save" color="primary" @click="onDialogOK(myEntity)" /> | |
</q-card-actions> | |
</q-card> | |
</q-dialog> | |
</template> | |
<script lang="ts" setup> | |
import { useDialogPluginComponent } from "quasar"; | |
import { ref } from "vue"; | |
import { openFormDialog } from "src/helpers/open-form-dialog"; | |
const { onDialogHide, dialogRef, onDialogOK } = useDialogPluginComponent(); | |
const props = defineProps({ | |
...openFormDialog.props, | |
// ... your props | |
}); | |
const myEntity = ref(props.initialValue); | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<q-dialog v-bind="dialogProps" v-on="dialogEvents"> | |
<q-card> | |
<!-- ... your content --> | |
<q-card-section> | |
{{ myEntity }} | |
</q-card-section> | |
<q-card-actions align="evenly"> | |
<q-btn v-close-popup label="Cancel" color="primary" /> | |
<q-btn label="Save" color="primary" @click="onDialogOK(myEntity)" /> | |
</q-card-actions> | |
</q-card> | |
</q-dialog> | |
</template> | |
<script> | |
import { defineComponent, ref } from "vue"; | |
import { useDialogWithDefaults } from "src/composables/use-dialog-with-defaults"; | |
import { openFormDialog } from "src/helpers/open-form-dialog"; | |
export default defineComponent({ | |
name: "MyFormDialog", | |
props: openFormDialog.props, | |
setup(props) { | |
const { onDialogHide, dialogRef, onDialogOK } = useDialogPluginComponent(); | |
const myEntity = ref(props.initialValue); | |
return { dialogProps, dialogEvents, onDialogOK }; | |
}, | |
}); | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div> | |
<q-btn label="Create" @click="showForm()" /> | |
<q-btn label="Edit" @click="showForm({ id: 10, title: 'test' })" /> | |
</div> | |
</template> | |
<script lang="ts" setup> | |
import { openFormDialog } from "src/helpers/open-form-dialog"; | |
import { MyEntity } from "./model"; | |
import MyFormDialog from "./my-form-dialog.vue"; | |
function showForm(initialValue?: MyEntity) { | |
openFormDialog<MyEntity>({ | |
component: MyFormDialog, | |
initialValue, | |
defaultValue: { title: "prova" }, | |
// eslint-disable-next-line no-console, @typescript-eslint/restrict-plus-operands | |
onSave: (payload: MyEntity) => console.log("onSave " + payload), | |
// eslint-disable-next-line no-console, @typescript-eslint/restrict-plus-operands | |
onCreate: (payload: MyEntity) => console.log("onCreate " + payload), | |
// eslint-disable-next-line no-console | |
onCancel: () => console.log("onCancel"), | |
// eslint-disable-next-line no-console | |
onDismiss: () => console.log("onDismiss"), | |
}); | |
} | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Dialog } from "quasar"; | |
import { Component } from "vue"; | |
const DEFAULT_INITIAL_VALUE_PROP_NAME = "initialValue"; | |
/* eslint-disable @typescript-eslint/no-explicit-any */ | |
interface OpenFormDialogOptions<T> { | |
component: Component; | |
initialValuePropName?: string; | |
initialValue?: T; | |
defaultValue?: T; | |
onSave?: (payload?: any) => Promise<void> | void; | |
onCreate?: (payload?: any) => Promise<void> | void; | |
onCancel?: () => Promise<void> | void; | |
onDismiss?: () => Promise<void> | void; | |
props?: Record<string, any>; | |
} | |
/* eslint-enable @typescript-eslint/no-explicit-any */ | |
/** | |
* Wrap `Dialog.create()` to provide a better DX, with specialized hooks based on the assumptions | |
* that dialog forms can be used either in creation or edit mode of a particular entity or array of entities. | |
* | |
* Use `openFormDialog.props` to define the `initialValue` prop into forms which will be opened by this helper. | |
* | |
* @param {object} options | |
* @param {import('vue').Component} options.component | |
* The component to open as a custom dialog | |
* @param {string} [options.initialValuePropName] | |
* Changes the name of the prop used to pass the initial value to the custom dialog component, which is `initialValue` by default. | |
* @param {*} [options.initialValue] | |
* If defined, will be provided to the component via `initialValue` prop. | |
* The form component is expected to create a local empty model for its purposes and populate it with the initial value. | |
* @param {*} [options.defaultValue] | |
* If `initialValue` option isn't defined, this option will be provided to the component via `initialValue` prop to set . | |
* @param {Function} [options.onSave] | |
* Runs when the dialog is closed via Quasar `onOk` hook, even when the dialog has been opened in creation mode. | |
* The dialog result is provided as parameter: if you're not interested into it, always define the hook as `() => {}` to avoid side effects. | |
* @param {Function} [options.onCreate] | |
* Runs when the dialog is closed via Quasar `onOk` hook and has been opened in creation mode, meaning when `initialValue` option isn't falsy. | |
* It is assured to run after `onSave` hook completes. | |
* The dialog result is provided as parameter: if you're not interested into it, always define the hook as `() => {}` to avoid side effects. | |
* @param {Function} [options.onCancel] | |
* Same as QDialog's hook with the same name. | |
* @param {Function} [options.onDismiss] | |
* Same as QDialog's hook with the same name. | |
* @param {object} [options.props] | |
* Add any custom dialog component prop. Avoid `component` prop name as it would overwrite `component` option. | |
* | |
* @example | |
* // Into the component that will be opened by the helper, when using Composition API and JS | |
* export default defineComponent({ | |
* name: 'UserFormDialog', | |
* props: { | |
* ...openFormDialog.props | |
* // ... your props | |
* } | |
* }) | |
* | |
* // OR, with script setup | |
* | |
* const props = defineProps({ | |
* ...openFormDialog.props | |
* // ... your props | |
* }) | |
* | |
* // Into the component using the helper | |
* const user = { id: 5, name: 'test', role: 'admin' } | |
* | |
* // Create | |
* async function showCreateForm() { | |
* openFormDialog({ | |
* component: UserFormDialog, | |
* defaultValue: { role: 'user' }, | |
* onCreate: () => { | |
* // ... | |
* }, | |
* }) | |
* } | |
* | |
* // Update | |
* async function showUpdateForm() { | |
* openFormDialog({ | |
* component: UserFormDialog, | |
* initialValue: user, | |
* onSave: () => { | |
* // ... | |
* }, | |
* }) | |
* } | |
* | |
* // Either create or update | |
* async function showForm(maybeUser) { | |
* openFormDialog({ | |
* component: UserFormDialog, | |
* initialValue: maybeUser, | |
* defaultValue: { role: 'user' }, | |
* onSave: () => { | |
* // ... | |
* }, | |
* onCreate: () => { | |
* // ... | |
* }, | |
* }) | |
* } | |
* | |
* // Use a custom initialValue prop name | |
* async function showUserForm() { | |
* openFormDialog({ | |
* component: CustomUserFormDialog, | |
* initialValuePropName: 'initialUserModel', | |
* initialValue: user, | |
* onSave: () => { | |
* // ... | |
* }, | |
* }) | |
* } | |
*/ | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
export function openFormDialog<T = any>({ | |
component, | |
initialValuePropName = DEFAULT_INITIAL_VALUE_PROP_NAME, | |
initialValue = undefined, | |
defaultValue = undefined, | |
onSave = () => undefined, | |
onCreate = () => undefined, | |
onCancel = () => undefined, | |
onDismiss = () => undefined, | |
props = {}, | |
}: OpenFormDialogOptions<T>) { | |
const isCreate = !initialValue; | |
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment | |
props[initialValuePropName] = initialValue ?? defaultValue; | |
Dialog.create({ | |
component, | |
componentProps: props, | |
}) | |
.onOk(async (result) => { | |
await onSave(result); | |
isCreate && (await onCreate(result)); | |
}) | |
.onCancel(onCancel) | |
.onDismiss(onDismiss); | |
} | |
openFormDialog.props = { | |
[DEFAULT_INITIAL_VALUE_PROP_NAME]: { | |
type: Object, | |
default: undefined, | |
}, | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useDialogPluginComponent } from "quasar"; | |
/** | |
* Wrap `useDialogPluginComponent` helper to also set appearance & behaviour common to all modals. | |
* Props and listeners must then be applied to a q-dialog component. | |
* | |
* @example | |
* // You can also destructure and use any method/property from `useDialogPluginComponent` | |
* const { dialogProps, dialogEvents } = useDialogWithDefaults() | |
* | |
* <q-dialog v-bind="dialogProps" v-on="dialogEvents" /> | |
*/ | |
export function useDialogWithDefaults() { | |
const dialogComposable = useDialogPluginComponent(); | |
const dialogProps = { | |
// Always make sure this name match the dialogComposable property name returned by the composable | |
// This is used to dynamically bind the ref to q-dialog component | |
ref: "dialogRef", | |
// ... add your own defaults, es.: | |
// persistent: true, | |
// maximized: true, | |
}; | |
const dialogEvents = { | |
hide: dialogComposable.onDialogHide, | |
}; | |
return { ...dialogComposable, dialogProps, dialogEvents }; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment