Handling multiple modals.

How can we handle multiple modals within the Nuxt app. I have separate modals for signup, login, password reset and some other things. But I'm unable to manage them globally. I have tried useState but it throws the error cannot stringify functions. Here are my implementations: usemodal works as a controller for single modal
export const useModal = (closeOtherModals: () => void) => {
const isOpen = ref(false);

function open() {
closeOtherModals();
isOpen.value = true;
}

function close() {
isOpen.value = false;
}

return {
isOpen: computed(() => isOpen.value),
open,
close,
};
};
export const useModal = (closeOtherModals: () => void) => {
const isOpen = ref(false);

function open() {
closeOtherModals();
isOpen.value = true;
}

function close() {
isOpen.value = false;
}

return {
isOpen: computed(() => isOpen.value),
open,
close,
};
};
define-modals defines all the modals available.
export default () => {
const modals = useState(
"modals",
(): { [k: string]: ReturnType<typeof useModal> } => {
return {};
},
);
const closeModals = () => {
let modal: keyof typeof modals.value;
for (modal in modals.value) {
if (modals.value[modal].isOpen.value) {
modals.value[modal].close();
}
}
};

modals.value = {
login: useModal(closeModals),
signup: useModal(closeModals),
forgotPassword: useModal(closeModals),
resetPassword: useModal(closeModals),
emailConfirmationSuccess: useModal(closeModals),
emailConfirmationFailed: useModal(closeModals),
bookingDetailsModal: useModal(closeModals),
profileCompletionModal: useModal(closeModals),
};
return {
loginModal: modals.value.login,
signupModal: modals.value.signup,
forgotPasswordModal: modals.value.forgotPassword,
resetPasswordModal: modals.value.resetPassword,
emailConfirmSuccessModal: modals.value.emailConfirmationSuccess,
emailConfirmFailedModal: modals.value.emailConfirmationFailed,
bookingDetailsModal: modals.value.bookingDetailsModal,
profileCompletionModal: modals.value.profileCompletionModal,
};
};
export default () => {
const modals = useState(
"modals",
(): { [k: string]: ReturnType<typeof useModal> } => {
return {};
},
);
const closeModals = () => {
let modal: keyof typeof modals.value;
for (modal in modals.value) {
if (modals.value[modal].isOpen.value) {
modals.value[modal].close();
}
}
};

modals.value = {
login: useModal(closeModals),
signup: useModal(closeModals),
forgotPassword: useModal(closeModals),
resetPassword: useModal(closeModals),
emailConfirmationSuccess: useModal(closeModals),
emailConfirmationFailed: useModal(closeModals),
bookingDetailsModal: useModal(closeModals),
profileCompletionModal: useModal(closeModals),
};
return {
loginModal: modals.value.login,
signupModal: modals.value.signup,
forgotPasswordModal: modals.value.forgotPassword,
resetPasswordModal: modals.value.resetPassword,
emailConfirmSuccessModal: modals.value.emailConfirmationSuccess,
emailConfirmFailedModal: modals.value.emailConfirmationFailed,
bookingDetailsModal: modals.value.bookingDetailsModal,
profileCompletionModal: modals.value.profileCompletionModal,
};
};
Inject/provide were not also helpful as state is not SSR friendly in that case.
9 Replies
Cue
Cue3mo ago
Could I ask, can and do your modals appear in parallel? Or can only one modal be open at any one time?
Muhammad Awais
Muhammad AwaisOP3mo ago
Only one modal at one time but one modal can open other modal
Jacek
Jacek3mo ago
Did you consider trying Pinia store instead? (example)
Cue
Cue3mo ago
In that case, you’re better off just storing modal states in modals and use a unified composable that toggles a modal based on a key.
Muhammad Awais
Muhammad AwaisOP3mo ago
Yups I believe that can work but I have moved to defining a plug-in. Plugin or a composable either way it's gonna work the same I thought of doing so but the store seems unnecessary here
Jacek
Jacek3mo ago
It is just a singleton. And you get nothing better than singletons to centralize logic.
Muhammad Awais
Muhammad AwaisOP3mo ago
Singleton a nice take but how do you think we should move with that, through a plugin or composable
Jacek
Jacek3mo ago
How do these plugins even work nowdays? I have dozen of custom plugins and they were great because they augmented this in every component... But there is no this in <script setup> so instead of being convenient, it just forces you to add 2 more lines (if example here is any indication). So you only really get the benefits when using plugged singleton in templates and in scripts based on options syntax. So it seems composables are just more straighforward and good enough for new projects. That said, I see nothing wrong in using both - having $modals for immediate use in @click handlers and composable for clean import anywhere else. It's not that you have too choose where the logic goes. Plugin file can just import composable itself AFAIK.
mrk
mrk3mo ago
Nested modals are not supported in v2, but they will be in v3. In the meantime, I created the following utility in case it helps:
export const openNested = async (callback: () => void) => {
useSlideover().close()
await new Promise(res => setTimeout(res, 1000)) // wait at least closing transition duration
callback()
}
export const openNested = async (callback: () => void) => {
useSlideover().close()
await new Promise(res => setTimeout(res, 1000)) // wait at least closing transition duration
callback()
}
then you can using e.g. as follows:
openNested(() => slideover.open(Slideover1, {
...props1,
onSubmit: async (event) => {
await openNested(() => slideover.open(Slideover2, ))
}
})
openNested(() => slideover.open(Slideover1, {
...props1,
onSubmit: async (event) => {
await openNested(() => slideover.open(Slideover2, ))
}
})
and you can chain them and use a closure to keep state of slideover 1, and pass it again once reopening it in onSubmit handler for slideover 2. The slideover components should then accept an onSubmit handler function or similar to call after being done
Want results from more Discord servers?
Add your server