diff --git a/doc/javascript-module-tutorial.rst b/doc/javascript-module-tutorial.rst new file mode 100644 index 00000000000..6180992b19c --- /dev/null +++ b/doc/javascript-module-tutorial.rst @@ -0,0 +1,255 @@ +Building a JavaScript Module +============================ + +CKAN makes heavy use of modules to add additional functionality to the +page. Essentially all a module consists of is an object with an +``.initialize()`` and ``.teardown()`` method. + +Here we will go through the basic functionality of building a simple +module that sends a "favourite" request to the server when the user +clicks a button. + +HTML +---- + +The idea behind modules is that the element should already be in the +document when the page loads. For example our favourite button will work +just fine without our module JavaScript loaded. + +:: + +
+ +Here it's the ``data-module="favorite"`` that tells the CKAN module +loader to create a new instance for this element. + +JavaScript +---------- + +Modules reside in the *javascript/modules* directory and should share +the same name as the module. We use hyphens to delimit spaces in both +filenames and modules. + +:: + + /javascript/modules/favorite.js + +A module can be created by calling ``ckan.module()``: + +:: + + ckan.module('favorite', function (jQuery, _) { + return {}; + }); + +We pass in the module name and a factory function that should return our +module object. This factory gets passed a local jQuery object and a +translation object. + +Initialisation +~~~~~~~~~~~~~~ + +Once ckan has found an element on the page it creates a new instance of +your module and if present calls the ``.initialize()`` method. + +:: + + ckan.module('favorite', function (jQuery, _) { + return { + initialize: function () { + console.log('I've been called for element: %o', this.el); + } + }; + }); + +Here we can set up event listeners and other setup functions. + +:: + + initialize: function () { + // Grab our button and assign it to a property of our module. + this.button = this.$('button'); + + // Watch for our favourite button to be clicked. + this.button.on('submit', jQuery.proxy(this._onClick, this)); + }, + _onClick: function (event) {} + +Event Handling +~~~~~~~~~~~~~~ + +Now we create our click handler for the button: + +:: + + _onClick: function (event) { + event.preventDefault(); + this.favorite(); + } + +And this calls a ``.favorite()`` method. It's generally best not to do +too much in event handlers it means that you can't use the same +functionality elsewhere. + +:: + + favorite: function () { + // The client on the sandbox should always be used to talk to the api. + this.sandbox.client.favoriteDataset(this.button.val()); + } + +Notifications and Internationalisation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This submits the dataset to the API but ideally we want to tell the user +what we're doing. + +:: + + options: { + i18n: { + loading: _('Favouriting dataset'), + done: _('Favourited dataset %(id)s') + } + }, + favorite: function () { + // i18n gets a translation key from the options object. + this.button.text(this.i18n('loading')); + + // The client on the sandbox should always be used to talk to the api. + var request = this.sandbox.client.favoriteDataset(this.button.val()) + request.done(jQuery.proxy(this._onSuccess, this)); + }, + _onSuccess: function () { + // We can perform interpolation on messages. + var message = this.i18n('done', {id: this.button.val()}); + + // Notify allows global messages to be displayed to the user. + this.sandbox.notify(message, 'success'); + } + +Options +~~~~~~~ + +Displaying an id to the user isn't very friendly. We can use the +``data-module`` attributes to pass options through to the module. + +:: + +