Working with modal windows

Let's look at how to create a modal window on the example of a simple plugin which user presses the button will open a modal window with a form.

Here is the code for this plugin:

(function($R)
{
    $R.add('plugin', 'myplugin', {
        init: function(app)
        {
            // define app
            this.app = app;

            // define services
            this.toolbar = app.toolbar;
        },
        start: function()
        {
            // create the button data
            var buttonData = {
                title: 'My plugin',
                api: 'plugin.myplugin.open'
            };

            // create the button
            var $button = this.toolbar.addButton('myplugin', buttonData);
        },
        open: function()
        {
            // open the modal
        }
    });
})(Redactor);

Now you need to add the body of the modal window to the properties of the plugin. To do this, we define the object modals, and inside we specify the modal window variable, by whose name we will open this window. This variable can contain any HTML code. But as a rule, it will be a form with a set of fields.

(function($R)
{
    $R.add('plugin', 'myplugin', {
        modals: {
            // this is variable with modal HTML body
            'mymodal': '<form action="">'
                + '<div class="form-item">'
                    + '<label>Please, type some text</label>'
                    + '<textarea name="text" style="height: 200px;"></textarea>'
                + '</div>'
            + '</form>'
        },
        init: function(app)
        {
            this.app = app;
            this.toolbar = app.toolbar;
        },
        start: function()
        {
            var buttonData = {
                title: 'My plugin',
                api: 'plugin.myplugin.open'
            };

            var $button = this.toolbar.addButton('myplugin', buttonData);
        },
        open: function()
        {
            // open the modal
        }
    });
})(Redactor);

By the way, you can specify several variables with modal windows, if your plugin should work with different windows. Like this:

(function($R)
{
    $R.add('plugin', 'myplugin', {
        modals: {
            // this is variable with modal HTML body
            'mymodal-1': '<form action="">'
                + '<div class="form-item">'
                    + '<label>Please, type some text</label>'
                    + '<textarea name="text" style="height: 200px;"></textarea>'
                + '</div>'
            + '</form>',
          'mymodal-2': '<form action="">'
               + '<div class="form-item">'
                    + '<label>Text</label>'
                    + '<input type="text" name="text">'
                + '</div>'
            + '</form>'
        },

        // ... plugin code ...

    });
})(Redactor);

Let's write the code for opening the modal window in the open method of our plugin:

open: function()
{
    var options = {
        title: 'My modal', // the modal title
        name: 'mymodal', // the modal variable in modals object
        commands: {
            cancel: { title: 'Cancel' } // the cancel button in the modal
        }
    };

    // open the modal with API
    this.app.api('module.modal.build', options);
}

Now when the button is pressed in the toolbar, our plugin will open the modal window. Full example:

(function($R)
{
    $R.add('plugin', 'myplugin', {
        modals: {
            'mymodal': '<form action="">'
                + '<div class="form-item">'
                    + '<label>Please, type some text</label>'
                    + '<textarea name="text" style="height: 200px;"></textarea>'
                + '</div>'
            + '</form>'
        },
        init: function(app)
        {
            this.app = app;
            this.toolbar = app.toolbar;
        },
        start: function()
        {
            var buttonData = {
                title: 'My plugin',
                api: 'plugin.myplugin.open'
            };

            var $button = this.toolbar.addButton('myplugin', buttonData);
        },
        open: function()
        {
            var options = {
                title: 'My modal', // the modal title
                name: 'mymodal', // the modal variable in modals object
                commands: {
                    cancel: { title: 'Cancel' } // the cancel button in the modal
                }
            };

            // open the modal with API
            this.app.api('module.modal.build', options);
        }
    });
})(Redactor);

Modal options #

When opening the modal window, we specified an object with options, let's look at all the possible options for calling the modal window.

  • title String
    • the modal title, shows in the modal header
  • name String
    • the variable name of modal in modals object
  • url String
    • url to file with modal body, if you want to get body HTML from file
  • width String
    • width of modal window, by default 600px
  • height String
    • height of modal window, by default is false, that means auto-height, you can set like 500px.
  • commands Object
    • the object with buttons in the modal window (see full description below)
  • handle String
    • a command from a commands object that will be called when the enter key is pressed in the form of a modal window (see full description below)

Let's see all the options in action:

open: function()
{
    var options = {
        title: 'My modal',
        name: 'mymodal',
        width: '500px',
        height: '400px',
        handle: 'insert',
        commands: {
            insert: { title: 'Insert' },
            cancel: { title: 'Cancel' }
        }
    };

    this.app.api('module.modal.build', options);
}

In this example, the modal window named mymodal will be opened with a width of 500px and a fixed height of 400px. In the modal window there will be two buttons:

  1. The cancel button that closes the modal window and calls the cancel callback of the modal window on closing.
  2. The insert button, which calls the callback of the modal window insert when pressed. Also in the handle option this command is specified, which means when you press enter key on the form, this command will also be called.

Commands #

The modal window commands, which are the same buttons that will be in the footer, are set using the commands object in the window's call options.

commands: {
    insert: { title: 'Insert' },
    cancel: { title: 'Cancel' }
 }

Each command has a name and its own object with the properties of the button. In properties, you can specify the type of the button, for example: danger, which will make the button red. This is useful for deletion actions.

commands: {
    insert: { title: 'Insert' },
    remove: { title: 'Delete', type: 'danger' },
    cancel: { title: 'Cancel' }
 }

Modal callbacks/messages #

Modal windows when opening and closing trigger common callbacks, regardless of which window is open or closed. See this in more detail in modal callbacks.

Also, each modal window calls its callbacks, tied to the modal window name and specified in the window's call options. Consider all the callbacks, for example, for this window call:

open: function()
{
    var options = {
        title: 'My modal',
        name: 'mymodal',
        handle: 'insert',
        commands: {
            insert: { title: 'Insert' },
            cancel: { title: 'Cancel' }
        }
    };

    this.app.api('module.modal.build', options);
}

Here are some callbacks that can be caught in your plugin by the name of this window:

(function($R)
{
    $R.add('plugin', 'myplugin', {
        modals: {
            'mymodal': '<form action="">'
                + '<div class="form-item">'
                    + '<label>Please, type some text</label>'
                    + '<textarea name="text" style="height: 200px;"></textarea>'
                + '</div>'
            + '</form>'
        },
        init: function(app)
        {
            this.app = app;
            this.toolbar = app.toolbar;
        },

        // messages
        onmodal: {
            mymodal: {
               open: function($modal, $form)
               {
               },
               opened: function($modal, $form)
               {
               },
               close: function($modal, $form)
               {
               },
               closed: function($modal, $form)
               {
               },
               cancel: function($modal, $form)
               {
               },
               insert: function($modal, $form)
               {
               }
            }
        },
        start: function()
        {
            var buttonData = {
                title: 'Myplugin',
                api: 'plugin.myplugin.open'
            };

            var $button = this.toolbar.addButton('myplugin', buttonData);
        },
        open: function()
        {
            var options = {
                title: 'My modal',
                name: 'mymodal',
                handle: 'insert',
                commands: {
                    insert: { title: 'Insert' },
                    cancel: { title: 'Cancel' }
                }
            };

            this.app.api('module.modal.build', options);
        }
    });
})(Redactor);

In the example, we specify the interception of messages from the modal windows through the onmodal object and in it we specify the name of the variable mymodal of our window.

The example shows a list of all possible messages/callbacks of this window, automatically called open, opened, close, closed and those messages that will be called when the buttons in the modal window are clicked: cancel, insert .

Each callback/message has the modal window object, and the form object as arguments (if the form is in the body of the modal window).

Create the life in our callbacks/messages of the modal window.

For example, let's set the data in the form of the modal window and put the focus in the form field.

// messages
onmodal: {
    mymodal: {
        open: function($modal, $form)
        {
            $form.setData({ text: 'Ok, it is my text.' });
        },
        opened: function($modal, $form)
        {
            $form.getField('text').focus();
        }
    }
}

To work with the form, we used Modal Form API.

Now let's see how the modal window can respond to commands, get data from the form, and close the window.

// messages
onmodal: {
    mymodal: {
        insert: function($modal, $form)
        {
            var data = $form.getData();
            this._insert(data);
        }
    }
}

In this example, when the Insert button is pressed in the modal window, the command triggers callback/message and inside we can use Modal Form API to get the data from the form and send them to the _insert plugin method. Let's look at the possible code for this method:

_insert: function(data)
{
    // close the modal
    this.app.api('module.modal.close');

    // check the data
    if (data.text.trim() === '') return;

    // insert data with Insertion Service
    this.insertion.insertText(data.text);
}

Modal translation #

Plugins have a service for translating values into different languages, including modal windows. To do this, define the lang service when initializing the plugin and set the values for the translation.

translations: {
    en: {
        "myplugin": "My Plugin",
        "myplugin-label": "Please, type some text"
    }
},
init: function(app)
{
    // define app
    this.app = app;

    // define lang service
    this.lang = app.lang;

    // define other services
    this.toolbar = app.toolbar;
    this.insertion = app.insertion;
}

Now call the lang service to specify the values.

// public
start: function()
{
	// create the button data
	var buttonData = {
		title: this.lang.get('myplugin'),
		api: 'plugin.myplugin.open'
	};

	// create the button
	var $button = this.toolbar.addButton('myplugin', buttonData);
},
open: function()
{
	var options = {
		title: this.lang.get('myplugin'),
		width: '600px',
		name: 'mymodal',
		handle: 'insert',
		commands: {
			insert: { title: this.lang.get('insert') },
			cancel: { title: this.lang.get('cancel') }
		}
	};

	this.app.api('module.modal.build', options);
}

You can specify language variables using the double # character in the HTML body of the window, for example:

modals: {
    'mymodal': '<form action="">'
        + '<div class="form-item">'
            + '<label>## myplugin-label ##</label>'
            + '<textarea name="text" style="height: 200px;"></textarea>'
        + '</div>'
    + '</form>'
}

In this case, the language variable myplugin-label will be automatically replaced with the value when the modal window is opened.

Full example #

Look at the full example: how to create a plugin with modal window.