OU blog

Personal Blogs

Moodle JavaScript (jQuery, AMD) for 2.8 developers

Visible to anyone in the world
Edited by Sam Marshall, Thursday, 11 Jun 2015, 12:13

This post is strictly for Moodle developers. If you're not a developer you can stop reading now as this is entirely technical - sorry about that. smile

In Moodle 2.9 the preferred way to write JavaScript is using jQuery and a module framework called AMD. This is documented in the Javascript Modules page of the Moodle developer documentation.

So what if you're writing a new plugin now, which currently has to run in 2.8, but you'd like to update it for 2.9 later with minimal work?

I did a hack to make this work (sort of). In case anyone else is interested, here's how...

1. Wrote my JavaScript using the 'new' approach, so that it begins:

define(['jquery'], function($) {

(For the purpose of this development, I can't currently specify any dependencies other than 'jquery' here; it's hard-coded.)

2. Added a simple wrapper that simulates the AMD module framework without implementing any of it. I put this in a file called define.js. Because this is all done within my plugin, I named the global variables according to my plugin's name. Here it is:

window.format_oustudyplan_modules = {};
window.format_oustudyplan_pending = [];
window.define = function(ignored, fn) {
    window.format_oustudyplan_pending.push(fn($));
};
window.format_oustudyplan_add_pending = function(module) {
    var next = window.format_oustudyplan_pending.shift();
    window.format_oustudyplan_modules[module] = next;
};
window.require = function(modules, fn) {
    var params = [];
    for (var i = 0; i < modules.length; i++) {
        params.push(window.format_oustudyplan_modules[modules[i]]);
    }
    fn.call(this, params);
};

3. Wrote a PHP function to 'load a module':

    public static function load($module) {
        global $PAGE;

        if (!self::$donefirst) {
            $PAGE->requires->jquery();
            $PAGE->requires->js(new \moodle_url('/course/format/oustudyplan/js/define.js'));
            self::$donefirst = true;
        }
        $PAGE->requires->js_init_code('format_oustudyplan_add_pending("' . $module . '");');
        $PAGE->requires->js(new \moodle_url('/course/format/oustudyplan/js/' . $module . '.js'));
    }

4. Wrote another one to 'call a module function':

    public static function call($module, $fn, array $params = array()) {
        global $PAGE;

        $jsparams = '';
        foreach ($params as $param) {
            if ($jsparams !== '') {
                $jsparams .= ',';
            }
            if (is_int($param)) {
                $jsparams .= $param;
            } else if (is_string($params)) {
                $jsparams .= '"' . addslashes_js($param) . '"';
            } else {
                throw new \coding_exception('Unexpected JS param');
            }
        }
        $PAGE->requires->js_init_code(
                'window.format_oustudyplan_modules.' . $module .'.' . $fn . '(' . $jsparams . ')');
    }

5 To include my JavaScript, I do this (the above two functions were in a 'js' class):

            js::load('sections');
            js::call('sections', 'init');

These PHP functions are not the same syntax as used in the 'real' function which is a single function js_call_amd, but are similar. The intention of this approach is that I should be able to move to 2.9 syntax without having to change my JavaScript code. What I will have to do:

  • Delete the PHP code above.
  • Move the JavaScript into the appropriately-named directory and update it using Grunt.
  • Change my loader code to use js_call_amd.

Hope this might be useful to somebody else smile

Permalink Add your comment
Share post