techie2k techie2k - 2 months ago 16
Javascript Question

Dojo Dialog with confirmation button

I would like to add a generic dialog with "Ok" and "Cancel" buttons support callback functions.

How can I achieve this with a Dojo AMD?

For example:

myDialog = new Dialog ({

title: "My Dialog",
content: "Are you sure, you want to delete the selected Record?",
style: "width: 300px",
onExecute:function(){ //Callback function
console.log("Record Deleted")
},
onCancel:function(){
console.log("Event Cancelled") }
});
// create a button to clear the cart
new Button({ label:"Ok"
onClick: myDialog.onExecute()
}

new Button({ label:"Cancel"
onClick: function(){ myDialog.onCancel() }
}

Answer

Here is the solution I came up when I had been facing the same question. It's not completely programmatic, but using the template makes the code more readable and flexible for changes.

Better to see once than to hear 100 times, so see all the code below live at jsFiddle: http://jsfiddle.net/phusick/wkydY/

The main principle I employ is the fact that dijit.Dialog::content may not only be a string, but also a widget instance. So I subclass dijit.Dialog to declare ConfirmDialog class. In ConfirmDialog::constuctor() I declare and instantize a widget from a template and set it to be dialog's content. Then I wire onClick actions in ConfirmDialog::postCreate() method:

var ConfirmDialog = declare(Dialog, {

    title: "Confirm",
    message: "Are you sure?",

    constructor: function(/*Object*/ kwArgs) {
        lang.mixin(this, kwArgs);

        var message = this.message;

        var contentWidget = new (declare([_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], {
            templateString: template, //get template via dojo loader or so
            message: message    
        }));
        contentWidget.startup();
        this.content = contentWidget;
    },

    postCreate: function() {
        this.inherited(arguments);
        this.connect(this.content.cancelButton, "onClick", "onCancel");
    }

})

The template markup:

<div style="width:300px;">

  <div class="dijitDialogPaneContentArea">
    <div data-dojo-attach-point="contentNode">
        ${message}              
    </div>
  </div>

  <div class="dijitDialogPaneActionBar">

    <button
      data-dojo-type="dijit.form.Button"
      data-dojo-attach-point="submitButton"
      type="submit"
    >
      OK
    </button>

    <button
      data-dojo-type="dijit.form.Button"
      data-dojo-attach-point="cancelButton"
    >
      Cancel
    </button>

  </div>

</div>

Now use ConfirmDialog instead of dijit.Dialog:

var confirmDialog = new ConfirmDialog({ message: "Your message..."});
confirmDialog.show();

Important: Do not forget to disconnect any connects to the dialogs callbacks and destroy dialog when closed.

If you use ConfirmDialog often and in multiple places of your code consider this:

var MessageBox = {};
MessageBox.confirm = function(kwArgs) {
    var confirmDialog = new ConfirmDialog(kwArgs);
    confirmDialog.startup();

    var deferred = new Deferred();
    var signal, signals = [];

    var destroyDialog = function() {
        array.forEach(signals, function(signal) {
            signal.remove();
        });
        delete signals;
        confirmDialog.destroyRecursive();
    }

    signal = aspect.after(confirmDialog, "onExecute", function() {
        destroyDialog();
        deferred.resolve('MessageBox.OK');
    });
    signals.push(signal);

    signal = aspect.after(confirmDialog, "onCancel", function() {
        destroyDialog();   
        deferred.reject('MessageBox.Cancel');            
    });
    signals.push(signal);

    confirmDialog.show();
    return deferred;
}

Your code will be more readable and you will not have to deal with cleaning up:

MessageBox.confirm().then(function() {
    console.log("MessageBox.OK")
});