Dialog with Parameterized

I am trying to implement a dialog component using Parameterized which can be used in template.modal:

class Dialog(param.Parameterized):
    action = param.String()
    on_confirm = param.Callable()
    on_cancel = param.Callable()

    def confirm(self, event):
        print("✅ Confirmed")
        self.on_confirm()
        template.close_modal()

    def cancel(self, event):
        print("🚫 Cancelled")
        if self.on_cancel is not None:
            self.on_cancel()
        template.close_modal()

    @param.depends("action", "on_confirm", "on_cancel")
    def prompt(self):
        confirm_button = pn.widgets.Button(name=self.action or "OK", button_type="danger")
        print(confirm_button)
        cancel_button = pn.widgets.Button(name="Cancel")
        confirm_button.on_click(self.confirm)
        cancel_button.on_click(self.cancel)

        return pn.layout.Column(
            pn.pane.Markdown(
                """
                # Are you sure?

                Sure you want to %s this row?
                """ % self.action.lower(),
            ),
            pn.layout.Row(
                self.action,
                confirm_button,
                cancel_button,
            ),
        )

and this is how I’d like to use it:

dialog = Dialog()

def confirm_delete():
    print("Delete confirmed! 💔")


def confirm_update():
    print("Update confirmed! ✏️")


template = pn.template.BootstrapTemplate(title="Odin")


def dialog_delete(event):
    print(event)
    dialog.on_confirm = confirm_delete
    dialog.action = "delete"
    template.open_modal()


def dialog_update(event):
    print(event)
    dialog.on_confirm = confirm_update
    dialog.action = "update"
    template.open_modal()


delete = pn.widgets.Button(name="Delete")
delete.on_click(dialog_delete)

update = pn.widgets.Button(name="Update")
update.on_click(dialog_update)

template.modal.append(dialog.prompt)
template.main.append(pn.Column("# 💬 Dialogs!", delete, update))
template.servable()
template.open_modal()

It almost works as but there I have two problems:

1. Button text incorrect

The first problem is that confirm_button never shows the the content of self.action, instead it’s always “OK”. I really don’t understand what’s going on, because when I do print(confirm_button) inside Dialog.prompt() the newly instantiated confirm_button does have the value of self.action which is either “delete” or “update” depending on which button opened the dialog, however this make no difference, the button that appears in the dialog still show “OK” on it. Also all text content of prompt is correct, and the button’s on_click calls the right callback, so it’s only the button text that is wrong.

2. Even handlers

  1. Click Delete button → dialog opens :white_check_mark:
  2. Click OK → dialog closes, and the confirm_delete callback is executed :white_check_mark:
  3. Click Delete button again → dialog opens, and confirm_delete callback is executed immediately :warning:

I don’t understand, why is the callback gets executed again?

For reference, I’ve been following Panel and Param — Panel v1.3.1 as a guide to use param and panel together to create this.

I would appreciate very much if someone could point me in the right direction, because I’m thoroughly puzzled.

I’ll have to look at the two issues you were experiencing more deeply but here’s how I’d write your component


from panel.viewable import Viewer

class Dialog(Viewer):
    action = param.String()
    on_confirm = param.Callable()
    on_cancel = param.Callable()

    def __init__(self, **params):
        super().__init__(**params)
        self._confirm_button = pn.widgets.Button(
            name=self._action_name, on_click=self.confirm, button_type="danger"
        )
        self._cancel_button = pn.widgets.Button(
            name="Cancel", on_click=self.cancel
        )

    def confirm(self, event):
        print("✅ Confirmed")
        self.on_confirm()
        template.close_modal()

    def cancel(self, event):
        print("🚫 Cancelled")
        if self.on_cancel is not None:
            self.on_cancel()
        template.close_modal()

    @param.depends('action')
    def _action_name(self):
        return self.action or 'OK'

    def __panel__(self):
        return pn.layout.Column(
            pn.pane.Markdown(
                """
                # Are you sure?

                Sure you want to %s this row?
                """ % self.action.lower(),
            ),
            pn.layout.Row(
                pn.pane.Markdown(self.param.action),
                self._confirm_button,
                self._cancel_button,
            ),
        )

dialog = Dialog()

template.modal.append(dialog)

and that seems to work correctly.

Thanks for the suggestion, but I am unable to make this work. Using your implementation and the callbacks dialog_delete and dialog_update are not executed at all (no log printed and the dialog doesn’t close)… :thinking:

Strange, here’s the full script I’m using:

import param
import panel as pn

from panel.viewable import Viewer


class Dialog(Viewer):
    action = param.String()
    on_confirm = param.Callable()
    on_cancel = param.Callable()

    def __init__(self, **params):
        super().__init__(**params)
        self._confirm_button = pn.widgets.Button(
            name=self._action_name, on_click=self.confirm, button_type="danger"
        )
        self._cancel_button = pn.widgets.Button(
            name="Cancel", on_click=self.cancel
        )

    def confirm(self, event):
        print("✅ Confirmed")
        self.on_confirm()
        template.close_modal()

    def cancel(self, event):
        print("🚫 Cancelled")
        if self.on_cancel is not None:
            self.on_cancel()
        template.close_modal()

    @param.depends('action')
    def _action_name(self):
        return self.action or 'OK'

    def __panel__(self):
        return pn.layout.Column(
            pn.pane.Markdown(
                """
                # Are you sure?

                Sure you want to %s this row?
                """ % self.action.lower(),
            ),
            pn.layout.Row(
                pn.pane.Markdown(self.param.action),
                self._confirm_button,
                self._cancel_button,
            ),
        )

dialog = Dialog()

def confirm_delete():
    print("Delete confirmed! 💔")


def confirm_update():
    print("Update confirmed! ✏️")


template = pn.template.BootstrapTemplate(title="Odin")


def dialog_delete(event):
    print(event)
    dialog.on_confirm = confirm_delete
    dialog.action = "delete"
    template.open_modal()


def dialog_update(event):
    print(event)
    dialog.on_confirm = confirm_update
    dialog.action = "update"
    template.open_modal()


delete = pn.widgets.Button(name="Delete")
delete.on_click(dialog_delete)

update = pn.widgets.Button(name="Update")
update.on_click(dialog_update)

template.modal.append(dialog)
template.main.append(pn.Column("# 💬 Dialogs!", delete, update))
template.servable()
1 Like