#Close dialog programatically if editDepth is > 1.

13 messages · Page 1 of 1 (latest)

green verge
#

Hey guys!

So, I have this file collection that can contain audio files, and each audio has a title, and I change the audio metadata (like the title or the cover image) based on what the user inputs in the other files.

I am doing this in a beforeChange hook, where I fetch the file from the req and apply the metadata and then append the newly created file to the req object, but when I am saving, if I try to immediatlly make another change, it prompts me with a pop up that tells that the files was change by another user and I need to reload the page. I guess that this happens because the file metadata is apply happening on the server and the client still has the old file and on edit it sees that.

There are 2 possibilites

  1. add/edit directly in the audio collection
  2. add/edit in a nested modal

In the 1st case, I have a client side SaveButton that calls router.refresh() and it works perfectly.

However, in the 2nd case, I can't find a good solution to refresh the data. I saw that each time the dialog is opened, the data is being refreshed, so i try to close the dialog after one update on the audio file.
One of the thing that seems to work is something like this to close the dialog programatically:

        ?.closest('dialog')
        ?.querySelector<HTMLButtonElement>('.doc-drawer__header-close');
      closeButton?.click();```

But this is fragile and it's not an appropriate solution.
I saw that there is a useModal() hook that exports closeModal, but it's not documented in the payloadCMS docs, and I don't really understand how it works.

So, my questions are:
1. How can I close the dialog programatically if the editDepth is > 1?
2. Is there any better wat to approach this issue, with the metadata file change and with the pop-up?
green verge
#

Oh and I saw that I have something like closeAllModals from the useModal() hook, but I can't use it because I can have unsaved data inside the nested dialogs and I will lose that data.

quick oar
#

If you create the drawer with useDocumentDrawer(), use the returned closeDrawer().
If you are inside the drawer content itself, get drawerSlug from useDocumentDrawerContext() and call useModal().closeModal(drawerSlug).

#

So for your second question, the better approach is usually:

  • Keep the metadata mutation in beforeChange if it must affect stored data. That part is correct.
  • Avoid any second update of the same audio doc during/after save. If you are doing a nested payload.update() or similar anywhere, that is the first thing I would remove.
  • In the nested modal case, use the drawer onSave to refetch the saved audio doc into the parent field state.
  • If you still want the drawer to fully re-open with fresh server state, call closeDrawer() after that refetch.
  • I would not use closeAllModals(). You’re right that it is too blunt and can wipe unrelated unsaved nested forms.
green verge
#

The thing is that I do not use a custom drawer, there are the drawers that are used automatically by payloadCMS.

I found that the useModal() hook exports an modalState object that can be used to take the slugs of the drawers.

        .reverse()
        .find((slug) => modalState[slug]?.isOpen);

      closeModal(activeModalSlug!);```

This is the custom code that I have used. I take the last modal (the one that I am currently in) and close it, with a custom `SaveButton` component with this code.
quick oar
#

modalState is global modal bookkeeping, so “take the last open modal and close it” depends on the current stacking/order behavior. If Payload opens another modal on top of the drawer later, your code could close the wrong thing.

#

For the automatic Payload document drawers, the cleaner mechanism is to use the drawer context, not the global modal list. Payload wraps drawer edit views in DocumentDrawerContextProvider, and the edit view itself reads from it:


const {
  clearDoc,
  drawerSlug,
  onDelete,
  onDuplicate,
  onRestore,
  onSave: onSaveFromContext,
} = useDocumentDrawerContext()

const isInDrawer = Boolean(drawerSlug)
#

So if your custom SaveButton is rendered inside the drawer’s edit view, you should be able to do:

import { useModal } from '@payloadcms/ui'
import { useDocumentDrawerContext } from '@payloadcms/ui'

const { closeModal } = useModal()
const { drawerSlug } = useDocumentDrawerContext()

if (drawerSlug) {
  closeModal(drawerSlug)
}

#

Your current activeModalSlug = last open modal approach is okay if you truly cannot access drawer context from where the button lives, but it is still a bit fragile.

#

Close the drawer after a successful save, not just on button click. Otherwise you can hide validation errors or close before the mutation finishes.

modern scaffold
#

@quick oar has the right idea here—good suggestion!

#

One thing to add: the drawerSlug from context will be undefined if the component is rendered outside a drawer (i.e. in the top-level edit view).

It might be a good idea to guard against that with something like:

const { drawerSlug } = useDocumentDrawerContext()
const { closeModal } = useModal()

// only close if we're actually in a drawer
if (drawerSlug) {
  closeModal(drawerSlug)
}