Magento 2 Certified Professional JavaScript Developer Guide
Section 2: Magento JavaScript Basics
2.1 Demonstrate understanding of the modular structure of Magento
What file structure is used to organize JavaScript modules? Where does Magento locate a module’s JavaScript file? Where does Magento store the JavaScript library?
Javascript files in Magento 2 can be located at the following places:- Library level (lib/web) – this directory is used solely for core Magento resources, available for all modules and themes
- Module level (app/code/Vendor/Module/view/AREA/web) – also available for all modules and themes
- Theme level for a particular module (app/design/AREA/Vendor/Theme/Vendor_Module/web) – available for current and inheriting themes
- Theme level (app/design/AREA/Vendor/Theme/web) – available for current and inheriting themes
Describe how static content is organized in Magento
Static content allows to redefine resources by simply creating a file with the same name, but on a level higher according to the fallback scheme. Magento 2 fallback system, in its turn, will search for the requested file following the directories in a certain order. This order depends on the file type and module context availability.For JavaScript files, as well as style files, images and fonts, the order is the following:- If the module context is unknown
- Static files of the current theme for a certain locale (THEME_DIR/web/i18n/LOCALE/)
- Static files of a current theme (THEME_DIR/web/)
- Parent themes until an unparented theme is not found (PARENT_THEME_DIR/web/i18n/LOCALE/, PARENT_THEME_DIR/web/)
- Library files (lib/web/)
- Current theme files for locale and module (THEME_DIR/web/i18n/LOCALE/Vendor_Module/)
- Current theme files for the module (THEME_DIR/Vendor_Module/web/)
- Parent themes, until an unparented theme is not found (PARENT_THEME_DIR/web/i18n/LOCALE/Vendor_Module/, PARENT_THEME_DIR/Vendor_Module/web/)
- Module files for the current AREA (app/code/Vendor/Module/view/AREA/web/)
- Module files for the base area (app/code/Vendor/Module/view/base/web/)
How does Magento expose a module’s static content to the web requests?
Due to the fact that static resources are used for creating a web page directly and therefore must be available at the user’s browser, Magento utilizes static content publishing mechanism.To make static files available externally, Magento publishes them in pub/static/frontend/VENDOR/THEME/LOCALE/ directory.Afterward, file app/code/Vendor/Module/view/frontend/web/js/customModule.js will be available at the path pub/static/frontend/VENDOR/THEME/MODULE/Vendor_Module/js/customModule.js.What are the different ways to deploy static content?
In developer and default modes static content directory is filled on-demand. In production mode, these files are not created automatically and must be deployed with CLI command setup:static-content:deploy.bin/magento setup:static-content:deploy [] bin/magento setup:static-content:deploy en_US en_GBThis command takes the input LOCALES list, for which one must publish static files, and allows to optionally exclude from the publication certain themes, locales, areas and file types.Additionally, there are several strategies for static resources publication:- Standard strategy – in this case, all files from all modules are published.
- Quick strategy – in this case, only one locale is published for each theme. In other locales, only altered files are published and the rest of them are copied. This strategy decreases deploy time and increases the number of files copied.
- Compact strategy – allows to avoid file duplicates by copying similar files in the directory base.
2.2 Describe How to use JavaScript modules in Magento
Use requirejs-config.js files to create JavaScript customizations
Requirejs-config.js holds an important place in Magento JavaScript ecosystem.The requirejs-config.js files are collected from the current theme, its parent themes, from all included modules into one file and the link to them is sent to the browser.Here is an example of requirejs-config.js file Magento (vendor / magento / module-catalog / view / frontend / requirejs-config.js):1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | var config = { map: { '*': { compareList: 'Magento_Catalog/js/list', relatedProducts: 'Magento_Catalog/js/related-products', upsellProducts: 'Magento_Catalog/js/upsell-products', productListToolbarForm: 'Magento_Catalog/js/product/list/toolbar', catalogGallery: 'Magento_Catalog/js/gallery', priceBox: 'Magento_Catalog/js/price-box', priceOptionDate: 'Magento_Catalog/js/price-option-date', priceOptionFile: 'Magento_Catalog/js/price-option-file', priceOptions: 'Magento_Catalog/js/price-options', priceUtils: 'Magento_Catalog/js/price-utils', catalogAddToCart: 'Magento_Catalog/js/catalog-add-to-cart' } }, config: { mixins: { 'Magento_Theme/js/view/breadcrumbs': { 'Magento_Catalog/js/product/breadcrumbs': true } } } }; |
How do you ensure that a module will be executed before other modules?
Bear in mind that the order of dependency listing does not correlate with the load order. To define the order of execution, one needs to apply Shim option in requirejs-config.js.For instance, jquery will be loaded before the jquery/jquery-migrate.1 2 3 | 'shim': { 'jquery/jquery-migrate': ['jquery'], }, |
1 2 3 4 5 6 7 8 9 | require(['jquery'], function ($) { ... }); define(['jquery'], function ($) return function(...){ ... }; }); |
How can an alias for a module be declared?
The best way to describe this course of action is with the following example:vendor/magento/module-ui/view/base/requirejs-config.js:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var config = { map: { '*': { uiElement: 'Magento_Ui/js/lib/core/element/element', uiCollection: 'Magento_Ui/js/lib/core/collection', uiComponent: 'Magento_Ui/js/lib/core/collection', uiClass: 'Magento_Ui/js/lib/core/class', uiEvents: 'Magento_Ui/js/lib/core/events', uiRegistry: 'Magento_Ui/js/lib/registry/registry', consoleLogger: 'Magento_Ui/js/lib/logger/console-logger', uiLayout: 'Magento_Ui/js/core/renderer/layout', buttonAdapter: 'Magento_Ui/js/form/button-adapter' } } }; |
What is the purpose of requirejs-config.js callbacks?
A callback is a function to execute after the deps loading. Callbacks are helpful in the situation when require is defined as a config object before require.js is loaded and one must specify a function to require after the loading of the configuration’s deps array.Describe different types of Magento JavaScript modules
A module is a separate *.js file that can import other require.js modules. It looks the following way:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // File (app/code/Vendor/Module/view/frontend/web/js/script.js) define(['jquery'], function ($) return function(...){ ... }; }); // Another module must look the following way to be applied by the first module. define([ 'jquery', 'Vendor_Module/js/script' ], function ($, myModule) return function(...){ ... myModule(...); ... }; }); |
Plain modules
A plain module is a regular one that is not inherited from another module. You can find an example of a plain module above (Vendor_Module/js/script). In the examples below, we will use modules that are inherited using the jQuery.widget, uiElement.extend functions.jQuery UI widgets
jQuery widget allows to create a UI widget with a custom handler. Let us consider the following example:vendor/magento/module-multishipping/view/frontend/web/js/payment.js:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | define([ 'jquery', 'mage/template', 'Magento_Ui/js/modal/alert', 'jquery/ui', 'mage/translate' ], function ($, mageTemplate, alert) { 'use strict'; $.widget('mage.payment', { options: { ... }, _create: function () { ... }, }); return $.mage.payment; }); |
UiComponents
UiComponents enables to create a widget with a custom handler. UiComponents are inherited from uiElement.Let us examine it using the following file as an example vendor/magento/module-catalog/view/frontend/web/js/storage-manager.js:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | define([ 'underscore', 'uiElement', 'mageUtils', 'Magento_Catalog/js/product/storage/storage-service', 'Magento_Customer/js/section-config', 'jquery' ], function (_, Element, utils, storage, sectionConfig, $) { 'use strict'; ... return Element.extend({ defaults: { ... }, initialize: function () { ... return this; }, }); }); |
2.3 Demonstrate ability to execute JavaScript modules
Magento javascript init methods is a default interface for launching RequireJS modules. It allows to avoid direct JavaScript introduction with using standard1 2 3 4 5 6 7 8 9 10 11 12 13 | <div id="example_div">Contents</div> |
1 2 3 4 5 | { "DOM_ELEMENT_SELECTOR": { "REQUIREJS_MODULE":CONFIG_OBJECT } } |
Contents
Also, if our component does not require DOM element to function, we can call it the following way:1 2 3 4 5 6 7 |
What is the purpose and syntax of the data-mage-init attribute?
Data-mage-init html attribute is an alternative method for initializing Magento JavaScript Component for a certain DOM element.How is it used to execute JavaScript modules?
We can modify our phtml file the following way:app/code/Vendor/Module/view/frontend/templates/test.phtmlContentThe result will be the same. Div with the same attribute and embedded config JSON object will be passed to the function, returned by Vendor_Module/test RequireJS module.It is necessary to use single quotation marks (data-mage-init=’…’) in this case, because the embedded JSON will be processed in a strict mode that requires JSON to have only double quotation marks.Also, in case JS component does not return the function, Magento tries to find REQUIREJS_MODULE in jQuery prototype, and if such does not exist, it will be called in the following way:1 2 | $.fn.REQUIREJS_MODULE = function() { ... }; return; |
What is the difference between the text/x-magento-init and the data-mage-init methods of JavaScript module execution?
Tag text/x-magento-init can be used to call modules without the reference to a certain DOM element.How do you execute a JavaScript module in an AJAX response and in dynamic code stored in a string?
To execute this, use imperative call of a JS module:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | require([ 'jquery', 'module' ],function($){ $(function(){ $('[data-role=example]') .accordion({ header: '[data-role=header]', content:'[data-role=content]', trigger:'[data-role=trigger]', ajaxUrlElement:"a" }); }); }); |
2.4 Describe jQuery UI widgets in Magento
Describe how Magento uses jQuery widgets, and demonstrate an understanding of the $.mage object
$.mage is a namespace for Magento widgets. It uses a set of widgets and functions; below is a list of the most common ones:- Accordion widget
- Alert widget
- Calendar widget
- Collapsible widget
- Confirm widget
- DropdownDialog widget
- Gallery widget
- List widget
- Loader widget
- Menu widget
- Modal widget
- Navigation widget
- Prompt widget
- QuickSearch widget
- Tabs widget
- __ – look translate
- cookies.get(name) – get cookie
- cookies.set(name, value, options) – change cookie
- cookies.clear(name) – clear cookie
- dataPost – a widget that creates post form and sends it to the server
- dateRange – date range widget that creates 2 calendar widget
- escapeHTML(str) – execute escape HTML
- formKey – widget that generates formKey and saves into cookie, in case formKey is absent from the cookie
- init – initiates data-mage-init scripts
- isBetween(value, from, to) – checks whether there is a number between two other
- isEmpty(value) – checks whether the line is empty using trim
- isEmptyNoTrim(value) – checks whether the line is empty not using trim
- isValidSelector(selector) – checks whether css selector is valid
- pageCache – a widget for full page cache that loads private content via an additional ajax query
- parseNumber(value) – transforms a line into a number
- redirect(url, type, timeout, forced) – executes redirect to the specified url
- sidebar – a sidebar widget
- stripHtml(value) – deletes html tags from the line
- translate – translates the line into the language of the current website locale
- translateInline – inline translation widget
- validation – form validation widget
What is the role of jQuery widgets in Magento?
jQuery JavaScript library serves to implement client functionality, widely using standard, customized and custom jQuery widgets for this purpose.A developer has a choice – either to create a new widget from scratch, using the $.Widget objects as a base to inherit from, or directly inherit from the existing jQuery UI or third-party widgets. If you name your custom widget the same as the widget you inherited it from, it is possible to extend the initial widget.How are Magento jQuery widget modules structured?
A widget is created using the $.widget method and contains options and ways to work with the widget (_addClass, _create, _delay, _destroy, _focusable, _getCreateEventData, _getCreateOptions, _hide, _hoverable, _init, _off, _on, _removeClass, _setOption, _setOptions, _show, _super, _superApply, _toggleClass, _trigger, destroy, disable, enable, instance, option, widget).The following example demonstrates how to create a widget using vendor/magento/module-multishipping/view/frontend/web/js/payment.js:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | define([ 'jquery', 'mage/template', 'Magento_Ui/js/modal/alert', 'jquery/ui', 'mage/translate' ], function ($, mageTemplate, alert) { 'use strict'; $.widget('mage.payment', { options: { ... }, _create: function () { ... }, }); return $.mage.payment; }); |
Describe how Magento executes jQuery widgets
Magento executes Magento jQueries two ways:- Connection via RequireJS
- data-mage-init and text/x-magento-init
1 2 3 4 5 6 7 8 9 10 | require( [ 'jquery', 'jquery/validate' ], function ($) { $.validator.addMethod("my-email", function(value, element) { return this.optional(element) || /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@(?:\S{1,63})$/.test(value); }, 'Please enter a valid email address.'); } ); |
How are Magento jQuery widgets executed with data-mage-init and text/x-magento-init?
To simplify the process of working with widgets in Magento, it is possible to initiate them. There are two alternative ways of initializing widgets:- data-mage-init = ”…” – attribute, on the basis of which the widget will be initialized
- script [type = “text/x-magento-init”] – a script tag that contains the widgets initialization data.
1 2 3 4 5 6 7 |
2.5 Demonstrate ability to customize JavaScript modules
Describe advantages and limitations of using mixins
Magento 2 provides the functionality that allows to listen to any RequireJS module after it was initialized and alter or moderate it immediately after initialization and before it is returned. This allows to redefine methods and add new parameters to JavaScript objects, not altering its original code.app/code/Vendor/Module/view/frontend/requirejs-config.js1 2 3 4 5 6 7 8 9 | var config = { 'config':{ 'mixins': { 'Magento_Customer/js/view/customer': { Vendor_Module/js/customer-mixin:true } } } }; |
1 2 3 4 5 6 7 8 | define([], function(){ 'use strict'; alert('Mixin is working'); return function(targetModule){ targetModule.newCustomProperty = value; return targetModule; }; }); |
1 2 3 4 5 | 'mixins': { 'REQUIREJS_MODULE': { MIXIN_MODULE:true } } |
1 2 3 4 5 6 7 8 9 10 11 | define([], function(){ 'use strict'; alert('Mixin is working'); return function(targetModule){ targetModule.defaultMethod = function(){ //custom replacement code } return targetModule; }; }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | define([], function(){ 'use strict'; alert('Mixin is working'); return function(targetModule){ return targetModule.extend({ defaultMethod:function() { var result = this._super(); //call parent method //custom code return result; } }); }; }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | define(['mage/utils/wrapper'], function(){ 'use strict'; alert('Mixin is working'); return function(targetModule){ var customFunction = targetModule.defaultFunction; var customFunction = wrapper.wrap(customFunction, function(original){ //custom code var result = original(); //original method call //custom code return result; }); targetModule.defaultFunction = customFunction ; return targetModule; }; }); |
What are cases where mixins cannot be used?
Mixins can not be used in case JS code was called without RequireJS.How can a new method be added to a jQuery widget?
To add a new method to a jQuery widget, use mixin:app/code/Vendor/Module/view/frontend/requirejs-config.js1 2 3 4 5 6 7 8 9 | var config = { "config": { "mixins": { "mage/dropdown": { Vendor_Module/js/dropdown-mixin':true } } } }; |
1 2 3 4 5 6 7 8 9 10 11 12 | define(['jquery'], function($){ return function(originalWidget){ $.widget('mage.dropdownDialog', $['mage']['dropdownDialog'], { //custom methods customMedhot:function(){ //custom code } }); return $['mage']['dropdownDialog']; }; }); |
How can an existing method of a jQuery widget be overridden?
You can also apply mixins to override an existing method of a jQuery widget.app/code/Vendor/Module/view/frontend/requirejs-config.js1 2 3 4 5 6 7 8 9 | var config = { "config": { "mixins": { "mage/dropdown": { Vendor_Module/js/dropdown-mixin':true } } } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | define(['jquery'], function($){ return function(originalWidget){ $.widget('mage.dropdownDialog', $['mage']['dropdownDialog'], { //overriden methods open:function(){ //custom code //parent method call return this._super(); } }); return $['mage']['dropdownDialog']; }; }); |
What is the difference in approach to customizing jQuery widgets compared to other Magento JavaScript module types?
jQuery widgets by default contain the features that allow to override and modify the widget. All we need to do is to override the widget:1 2 3 4 5 6 | //original definition jQuery.widget('widgetNamespace.methodName', {/* ... initial method definitions ... */}); /extending widget jQuery.widget('widgetNamespace.methodName', jquery.widgetNamespace.methodName, {/*... new method definitions here ...*/}); |
- Call of the corresponding RequireJS module
- RequireJS module defines the widget (jQuery.define)
- RequireJS module returns the newly created widget
- Magento core applies the newly created widget