Javascript and YUI 3 FAQ

Jump to: navigation, search

Note: You are currently viewing documentation for Moodle 1.9. Up-to-date documentation for the latest stable version is available here: Javascript and YUI 3 FAQ.

Moodle 2.0

A quick primer on the internals of Moodle's javascript, and how to use YUI within it. Based on formlibs plus ajax discussion in the General Developer forum.

How do I add an event to make changing a value in a select control submit its parent form?

When you put the field's name in addElement, it automatically gives it an ID. Say you called it "select1", the id would be "id_select1". There's an exception to this (I can't remember which element it is) in which case you'll need to define an id in the attributes.

You'll then want to get the select and the form using YUI, and attach an event listener:

Y.on('change', submit_form, '#select1');
 function submit_form() {    
    var form = Y.one('#mform1'); // The id for the moodle form is automatically set.
    var form.submit();
 }

This code would normally live in a file called 'module.js' within your plugin directory. See other FAQs for actually accessing this.

Explanation:

  • The first line sets up the event listener. The first parameter says watch for a 'change' of value. The second is the name of the callback function to execute when the change happens. The third is the name of the DOM element that is being watched (in this case the select form element with id='select1'. The second parameter matches the name of the callback function below.
  • For a full explanation of event listeners see http://developer.yahoo.com/yui/3/event/
  • The callback function 'submit_form' uses YUI's methods to access the form in which the select box lives (in this case with id="form1") and then submits it. For a full explanation of the Y.one method see http://developer.yahoo.com/yui/3/node
  • When the form node has been obtained, the node's submit method is used to submit the form. See the API documentation for YUI's node class for this and other node methods - http://developer.yahoo.com/yui/3/api/Node.html

How do I get that into the page. Is that a 'yui' call or a 'requires_js" call or whatever?

The best documentation to read is here: Development:JavaScript_guidelines

Essentially, you need to define M.your_plugin as an object in a javascript file. You then define the submit_form() function as a method of M.your_plugin, and put the Y.on() call in the init method of the object. You then include the javascript file with $PAGE->requires_js() and call the init method with js_init_call (see docs).

Also, where does the 'Y' get defined? Is this documented somewhere (I can't find anything)?

Y is a bit complicated, it took me a while to really understand what happens, but here's a brief explanation:

Y is a namespace generated when YUI is initialised containing the methods from all the loaded modules. Internally, moodle initialises Y and passes it to the module's init function in js_init_call, as the first parameter. To access Y from your init function, you need to do this:

M.my_plugin.init = function(Y) { //Y is now accessible in the scope of init().

However, you probably want access Y in your object's other method's too, so you need to do something like this:

M.my_plugin.init = function(Y) { //Y is now accessible in the scope of init().
    this.Y = Y; // this.Y is now accessible from all methods of M.my_plugin
 }

For tidiness, I always start any other method using Y with "var Y = this.Y" so I don't have to keep prefixing with this.

You may also find this page useful (or more confusing ) Development:How_to_create_a_YUI_3_module

The M.your_plugin thing. What if it isn't a module? What if it's a report somewhere or a local plugin. What's the syntax for this thing? I guess the 'M' is just an arbitrary "thing"

First of all, disambiguation of terms:

  • A YUI Module is subset of YUI functionality, contained in it's own namespace, e.g Y.Node
  • A Moodle Module, I'm sure you understand.
  • A Javascript Module in this context, is a subset of javascript functionality contained in it's own M. namespace.

Any Moodle plugin can define it's own Javascript Module, whether it's a Moodle Module or a different type of plugin. Usually this will be in the plugin's module.js file, and be called M.plugintype_pluginame (similar to the lang file naming convention), but the object passed as the 4th parameter of $PAGE->requires->js_init_call() allows you to customise this.

A good example of Y's usage that I can point you to is my quickfindlist block.

Take a look at the bottom of get_content() in block_quickfindlist.php. This defines $jsmodule (the Javascript module's parameters, including the name, the file it's stored in, and the YUI modules it needs), $jsdata (extra parameters to pass with the init call, in addition to Y), and then passes them, along with the name of the module's init function, to js_init_call(). Note that as it's a block, it uses $this->page in place of $PAGE.

Now look at module.js. It defines M.block_quickfindlist (which is the module we specified with $jsmodule) and the init() method (which we referenced in js_init_call()). init accepts 6 parameters: Y, and the 5 set in $jsdata. It then stores a copy of Y in M.block_quickfindlist.Y so it can be used in other methods, and uses Y itself to call Y.on(), adding event listeners to the block. M.block_quickfindlist.search() is defined futher down module.js - it needs acces to Y too, so we create a copy in the method's scope for easy referencing, before using it to make an AJAX request with Y.io(). You can also see use of M.cfg, another Javascript Module, to access the value of wwwroot.

An important thing to note about Y being used all over the place is that as I understand it, Y is only initialised once with all the YUI Modules required by any Javascript Modules on the current page (hence the requirements manager). It's then passed to each module's init function, so they can use it in their own scope.

The actual "core" javascript code to do this would look something like:

YUI.use('required', 'yui', 'modules', function(Y) { 
    // YUI.use initialises Y with required modules, 
    // and passes it to an anonymous function.
    // Y is then available to pass to our own init functions
    M.block_blockname.init_function(Y);
    M.report_reportname.init_function(Y, other, parameters);
 });

How does Y relate to the yui docs? To step back, how do I work out what methods etc. Y has that I can use?

When you request a module for inclusion, it's classes will be attached to the Y namespace. You can see it's classes by going to the YUI documentation for that module, and looking at the API docs. Each module's classes then has a set of properties and methods.

For example the IO module contains the io class. So telling Moodle that your javascript module requires io will give you access to Y.io and all of its methods. In general, reading the YUI module's documentation and it's examples will give you a good general idea of how to use it's functionality.

It's also worth noting that some modules will create a shortcut method attached directly to Y for convenience. For instance Y.io.io() can also be called using Y.io(), and Y.Node.one() can be called using Y.one().

I'm guessing that some YUI modules are loaded by default ('base' and 'node' probably are), but the requirements manager should make sure that each one's only loaded once. I always define all the YUI Modules my Javascript Module needs, just to make sure.

It doesn't work. Now what?

Firstly, make sure you have debugging on your site set to Developer (and messages displayed). Without this level of debugging any detected errors will fail silently.

See also