Magento 2 JavaScript CheatSheet

JavaScript CheatSheet for Magento 2 covering different concepts on initialisation, jQuery Widgets, UI Components and overriding methods. A quick go to reference to see the different ways of handling JavaScript.

If you’re looking for something more in depth checkout some of these articles, plain modules, jQuery UI widgets and UI Components.

Initialisation

There are three main ways to initialise a JavaScript module using either imperative or declarative.

Imperative

<script>
require([
'jquery'
], function($) {
'use strict';

$(function() { // document.ready shorthand
console.log('loaded');
});
});
</script>

Declarative

<script type="text/x-magento-init">
{
"*": {
"Magento_Theme/js/example": {}
}
}
</script>
<div class="selector" data-mage-init='{
"Magento_Theme/js/example": {}
}'>
<p>Example JavaScript Module</p>
</div>

Plain Modules

Regular module, call define function and include the callback within it. This callback returns another function, use it with data-mage-init attribute or text/x-magento-init script. The config and element is optional which gets passed through from the initialisation.

define([], function () {
'use strict';

var module = function (config, element) {
console.log(config, element);
};

return module;
});

jQuery UI Widgets

Declares a jQuery UI widget using the widget factory, always return the newly created widget.

define([
'jquery',
'jquery/ui'
], function ($) {
'use strict';

$.widget('namespace.widgetname', {
options: {
// default options
// options passed into the widget override these
},

_create: function () {
console.log(this.options);
console.log(this.element);
},

// Public method
publicMethod: function () {},

// Private method (begin with underscore)
_privateMethod: function () {},
});

return $.namespace.widgetname;
});

Add New Method

Creating a new method to be added to an existing jQuery widget should be done with a mixin. The original widget is passed via a parameter where a brand new widget is created.

requirejs-config.js

var config = {
config: {
mixins: {
'mage/dropdown': {
'Magento_Theme/js/dropdown-mixin': true
}
}
}
};

Magento_Theme/web/js/dropdown-mixin.js

define([
'jquery'
], function ($) {
'use strict';

return function (originalWidget) {
$.widget('mage.dropdownDialog', originalWidget, {
// add new methods
});

return $.mage.dropdownDialog;
}
});

Override Existing Method

Adding functionality to an existing method is almost the same as creating a new method except these methods should have this._super(); to call the parent method.

define([
'jquery'
], function ($) {
'use strict';

return function (originalWidget) {
$.widget('mage.dropdownDialog', originalWidget, {
open: function () {
// call the parent method
this._super();

// add our new functionality
console.log('dropdown opened');
}
});

return $.mage.dropdownDialog;
}
});

Create New Widget

Create a new widget from scratch:

define([
'jquery',
'jquery/ui'
], function ($) {
'use strict';

$.widget('jason.mywidget', {
// add functionality
});

return $.jason.mywidget;
});

Create a new widget based off an existing widget:

define([
'jquery',
'jquery/ui',
'mage/dropdown'
], function ($) {
'use strict';

$.widget('jason.mywidget', $.mage.dropdownDialog, {
// add functionality
});

return $.jason.mywidget;
});

UI Components

UI component uses the Knockout JS library to bind data to certain DOM elements on the page which then gets displayed to the user.

Initialisation

<div id="selector" data-bind="scope: 'knockout-example'">
<h1 data-bind="text: heading"></h1>
<!-- ko template: getTemplate() --><!-- /ko -->
</div>

<script type="text/x-magento-init">
{
"#selector": {
"Magento_Ui/js/core/app": {
"components": {
"knockout-example": {
"component": "Magento_Theme/js/knockout-view-model",
"config": {
"template": "Magento_Theme/knockout-template",
"heading": "Hello World!"
}
}
}
}
}
}
</script>

The more common method is to get the configuration from layout XML:

Magento_Theme/templates/example.phtml

<script type="text/x-magento-init">
{
"#selector": {
"Magento_Ui/js/core/app": <?= /* @escapeNotVerified */ $block->getJsLayout() ?>
}
}
</script>

Magento_Theme/layouts/example.xml

<block name="example" template="Magento_Theme::example.phtml">
<arguments>
<argument name="jsLayout" xsi: type="array">
<item name="components" xsi:type="array">
<item name="knockout-example" xsi:type="array">
<item name="component" xsi:type="string">Magento_Theme/js/knockout-view-model</item>
<item name="config" xsi:type="array">
<item name="template" xsi:type="string">Magento_Theme/knockout-template</item>
<item name="heading" xsi:type="string">Hello World!</item>
</item>
</item>
</item>
</argument>
</arguments>
</block>

Magento_Theme/web/js/knockout-view-model.js

define([
'uiComponent',
'ko'
], function (Component, ko) {
'use strict';

return Component.extend({
defaults: {
// default options
// options passed into the component override these
},

initialize: function () {
// add custom functionality
// executed only once
this._super();
}
});
});

Add New Method

Creating a new method to be added to an existing UI Component should be done with a mixin. The original Component is passed via a parameter where a brand new component is created.

requirejs-config.js

var config = {
config: {
mixins: {
'Magento_Checkout/js/view/minicart': {
'Magento_Checkout/js/view/minicart-mixin': true
}
}
}
};

Magento_Checkout/web/js/view/minicart-mixin.js

define([], function () {
'use strict';

return function (Component) {
return Component.extend({
// add new methods
});
}
});

Override Existing Method

Adding functionality to an existing method is almost the same as creating a new method except these methods should have this._super(); to call the parent method.

define([], function () {
'use strict';

return function (Component) {
return Component.extend({
initialize: function () {
// call the parent method
this._super();

// add our new functionality
console.log('minicart initialised');
}
});
}
});

Wrapping Function Calls

Wrapping function calls is another approach when you want to override existing functionality, this can only be used with plain modules that return an object or a function. For jQuery widgets and UI components use the methods already described above.

The need of the wrapper module is to wrap an existing function call with new functionality without needing to edit the original function. It also allows multiple people to wrap the same function/object (unlike the other methods above).

requirejs-config.js

var config = {
config: {
mixins: {
'Magento_Theme/js/example': {
'Magento_Theme/js/example-mixin': true
}
}
}
};

Magento_Theme/web/js/example.js

define([], function () {
'use strict';

var moduleName = function (config, element) {
console.log('original module');
};

return moduleName;
});

Magento_Theme/web/js/example-mixin.js

define([
'mage/utils/wrapper'
], function (wrapper) {
'use strict';

return function (originalModule) {
return wrapper.wrap(originalModule, function (original, config, element) {
// make changes
console.log('change before original');

// call original method
var result = original();

// make changes
console.log('change after original');

// return original
return result;
});
};
});

In the example above we use the mage/utils/wrapper to wrap the original module with a new function. The wrap method accepts two arguments, the first being the original function you want to wrap and the second being the new function you want to wrap it with.

In the new function the original module is available to us along with any other arguments that we can grab from the original module, like config and element.

When the original module returns an object you can wrap an individual method like the example below.

define([], function () {
'use strict';

var moduleName = {
methodOne: function () {
console.log('method one called');
}
};

return moduleName;
});
define([
'mage/utils/wrapper'
], function (wrapper) {
'use strict';

return function (originalModule) {
var newMethodOne = wrapper.wrap(originalModule.methodOne, function (original) {
// make changes
console.log('change before original');

// call original method
var result = original();

// make changes
console.log('change after original');

// return original
return result;
});

originalModule.methodOne = newMethodOne;
};
});