Hi folks,
It's been a big few years in Moodle for Javascript. We've migrated to support of the newer ES6-style Javascript modules (MDL-62497 since Moodle 3.8), dropped support for Internet Explorer (MDLSITE-6109 since Moodle 3.10), added support for sub-directories in Javascript (MDL-66192 since Moodle 3.8), and we are constantly trying to improve the landscape for our developers through a range of other micro and macro changes.
I know that many of you are enjoying the newer style of JS module, and finding them nicer and easier to write.
One of the changes that I have been looking at recently is to simplify the generation and consumption of Javascript events.
At present we have a number of custom event systems in use in core, these include:
- YUI Custom Events (e.g. `Y.publish('someCustomEvent');`);
- jQuery Custom Events (e.g. `$(document).trigger('someCustomEvent');` and `$(document).on('someCustomEvent', * someHandler);`);
- the `core/custom_interaction_events` module, which is an extension of the jQuery Custom Events;
- the `core/pubsub` module (e.g. `PubSub.publish('someCustomEvent', {some: 'custom', data});` and `PubSub.subscribe('someCustomEvent', someHandler);`); and
- use of native custom events (e.g. `document.addEventListener('someCustomEvent', someHandler);` and `document.dispatchEvent(new CustomEvent('someCustomEvent', {detail: {some: 'custom', data}}));`).
Sadly none of these are very compatible with one another and the presence of some of the older YUI events in particular cause problems. Additionally it's not clear which should be used for new code.
To try and simplify things and make it clearer for new code, I have been looking at a way to migrate away from these various different approaches and to recommend a single approach for all custom events.
The suggestion I am proposing is to migrate the use of all of the above to only use the native custom events. You can read about how to use, trigger, and consume native custom events on the MDN.
This migration will include:
- deprecation of all custom YUI events (primarily the `moodle-core-events` YUI module) via MDL-70990;
- deprecation of all custom jQuery events located in `core/event` via MDL-70990;
- migration of the `core/custom_interaction_events` to use native events (MDL to be confirmed); and
- allow publishers of `core/pubsub` to plan migration to use native events (MDL to be confirmed).
Deprecation of each of these older systems will each include a migration path to allow the publisher to migrate to the native events, including publishing deprecation notices to the developer console in some cases.
The first issue in this chain is MDL-70990 and it includes a helper module: `core/event_dispatcher` which exports a helper function `dispatchEvent` which is a wrapper around EventTarget.dispatchEvent function and fills in some of the defaults, and sets the default target to `document`.
It also proposes a structure for events putting all event details for a component into a single location: `[component]/events` which exports a list of `eventTypes` and, optionally, some notification functions. For example:
// lib/form/amd/src/events.js import {dispatchEvent} from 'core/event_dispatcher'; /** * Event types for the `core_form` subsystem. * * @const * @property {string} formFieldValidationFailed An event triggered upon form field validation failure. */ export const eventTypes = { formFieldValidationFailed: 'core_form/formFieldValidationFailed', }; export const notifyFormFieldValidationFailed = (field, message) => dispatchEvent(eventTypes.formFieldValidationFailed, { message, }, field, { cancelable: true, } );
A module wishing to publish notify or publish the event can do so easily:
import {notifyFormFieldValidationFailed} from 'core_form/events'; // ... const someFieldInTheForm = document.querySelector('#someFormId input'); notifyFormFieldValidationFailed(someFieldInTheForm, 'The form validation failed for reason x');
Any code which wishes to listen to the above event can do something like the following:
import {eventTypes as formEvents} from 'core_form/events'; // ... const myForm = document.querySelector('#someFormId'); myForm.addEventListener(formEvents.formFieldValidationFailed, e => { window.console.log(e.detail); // "The form validation failed for reason x" });
I appreciate any feedback that you may have,
Andrew