Magento 2 Certified Professional JavaScript Developer Guide
Section 1: Technology Stack
1.1 Demonstrate understanding of RequireJS
What is the main purpose of the RequireJS framework?
RequireJS library is a file and module loader that implements AMD API (Asynchronous Module Definition). AMD API is used as a specification? with an aim to define modules such that the module and its dependencies can be asynchronously loaded.Which capabilities does RequireJS provide to create and customize JavaScript modules?
Below is the example of RequireJS library usage:1 2 3 4 | require(['vendor/library'], function(library) { let text = library.getText(); alert(text); }); |
1 2 3 4 5 6 7 8 | define([], function(){ var library = {}; library.getText = function() { return 'Hello World'; } return library; }); |
1 2 3 | require.config({ baseUrl: '/scripts', }); |
What are the pros and cons of the AMD approach to JavaScript file organization?
The main advantage of RequireJS is that it allows not to worry about how and from where the needed files will be loaded. Instead, the developer will specify the module X as a dependency, and RequireJS will do all the work.RequireJS also allows to separate JavaScript code into independent modules and load them only when necessary.The major disadvantage of RequireJS is the necessity to wrap JS code into requirejs methods (define and require). This may particularly come as a hurdle during legacy modules adaptation.Another con of the AMD approach to JavaScript file organization is a large number of HTTP requests, because each module is loaded separately.RequireJS in Magento 2
Magento 2 uses RequireJS for loading all the JavaScript code. By default, it contains the RequireJS call and a launch configuration, as well as the mechanism for expanding this configuration.Out-of-the-box Magento 2 loads RequireJS modules into /static/AREA/THEME_VENDOR/THEME_NAME/LOCALE. This path is also used to publish frontend static assets from Magento modules. Which means that if our module contains, for instance, app/code/Vendor/Module/view/adminhtml/web/library.js file, it will be automatically published as /static/adminhtml/Magento/backend/en_US/Vendor_Module/library.js and then can be used as RequireJS module:1 2 3 4 5 |
What is a requirejs-config.js file?
The key function of requirejs-config.js files is to allow adding changes into RequireJS library configuration.Duringsetup:static-content:deploy, Magento merges all the require-config.js module files for corresponding areas and creates a combined file that contains all of the requirejs config.In which cases it is necessary to add configurations to it?
To make an example, we will create a new requirejs-config.js file:app/code/Vendor/Module/view/frontend/requirejs-config.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var config = { paths:{ "customLibrary":"Vendor_Module/library" } }; This file will be merged into the combined requirejs-config.js file in the following format: (function() { var config = { paths:{ "customLibrary":"Vendor_Module/library" } }; require.config(config); })(); |
What tools does it provide?
Aliases
Alias can be created for any requirejs module:1 2 3 4 5 | require.config({ paths: { "customLibrary": "vendor/library" }, }); |
1 2 3 4 | require(['customLibrary'], function(library) { Let text = library.getText(); alert(text); }); |
Shim
Many libraries like jQuery, that were developed earlier than RequireJS or AMD standard, have a custom plugin system. Such a system is based on the modification of a single global library object, while additional modules are connected with separate JS files that would interact with this global object.In the case with RequireJS, the global jQuery object will not be created until the respective RequireJS module is called; otherwise, the jQuery may turn out to be not defined during the load process.Apply RequireJS configuration in order to add jQuery plugin into Magento 2:app/code/Vendor/Module/view/frontend/requirejs-config.js1 2 3 4 5 | var config = { paths:{ "jquery.plugin":"Vendor_Module/jquery.plugin.min" } }; |
1 2 3 4 5 | var config = { paths:{ "jquery.plugin":"Vendor_Module/jquery.plugin.min" } }; |
1 2 3 | require(['jquery','jquery.plugin], function(jQuery, jQueryPlugin){ //customCode }); |
1 2 3 4 5 6 7 8 9 10 | var config = { paths:{ "jquery.plugin":"Vendor_Module/jquery.plugin.min" }, shim:{ 'jquery.plugin:{ 'deps':['jquery'] } } }; |
What are global callbacks?
Global callbacks allow to specify the method one must call after all the dependencies are loaded.How can mappings be used?
Map directive allows to selectively change modules and their locations.What are RequireJS plugins?
RequireJS loader plugins allow to load various dependencies, as well as optimize them if necessary.Such plugins are marked with ! sign before the module name1 2 | require(['loaderPlugin!moduleToBeLoaded'], function(module) { }); |
What are the text and domReady plugins used for?
Text plugin allows to load text files, for instance, html templates.1 2 3 4 5 | require(["module", "text!template.html"], function(module, html) { //html variable will contain text from template.html file } ); |
1 2 3 | require(['domReady!'], function () { //code will be executed only when DOM is ready }); |
1.2 Demonstrate understanding of UnderscoreJS
Demonstrate understanding of utility functions
UnderscoreJS is a cross-browser library that provides additional functions for working with arrays, objects and functions, templates and more.UnderscoreJS connects to RequireJS as ‘underscore’ and is usually used as ‘_’.Example:1 2 3 4 5 6 | define ([ 'underscore' ], function (_) { var max = _.max ([1, 2, 3]); return max; }); |
What are the benefits of using the underscore library versus native javascript?
UnderscoreJS library provides functions for certain common tasks, which greatly simplifies the developers’ work, especially in older browsers. However, currently more and more browsers supports native JavaScript functions, which raises the question – which is more superior?Therefore, if some function requires the support of older browsers, then it is better to rely on UnderscoreJS library. If not, then use native JavaScript; this way, script execution speed will be higher.Use underscore templates in customizations
The Underscore template compiles JavaScript templates into functions that can be used for rendering.The example of usage:_.template (templateString, [settings])Describe how underscore templates are used
<% = …%> substitutes the result of expression calculation<% – …%> substitutes the HTML escaped calculation result<% …%> calculates an expression and does not substitute anythingMagento uses underscore templates in the mage/template script. However, it is used less frequently then KnockoutJSWhat are the pros and cons of underscore templates?
Advantages of underscore templates:- support in older browsers,
- the ability to include logical expressions in templates.
- does not support internationalization (i18n),
- does not support observables.
Describe how the underscore templates are used in Magento together with the text RequireJS plugin
The use of underscore templates we will describe using the Magento_Ui/js/lib/knockout/bindings/tooltip.js file as an example.1 2 3 4 5 6 7 8 9 10 11 12 | define ([ ... 'mage / template', 'text! ui / template / tooltip / tooltip.html', ... ], function (..., template, tooltipTmpl, ...) { ... template (tooltipTmpl, { data: config } ... } |
- RequireJS text plugin connects text resources and is used by the mask ‘text!path-to-text-resource’
- RequireJS text plugin substitutes the contents of a text resource into a tooltipTmpl parameter
- mage/template is called with template text and data.
- mage/template calls the underscore template
1.3 Demonstrate understanding of jQuery UI widgets
A large part of Magento 2 frontend is built with jQuery Widgets. Using either standard or custom jQuery UI widgets, one can create menus, modal windows, confirmation dialogues, and many other elements.What is a jQuery library?
A standard jQuery widget call looks the following way:$('#tabs-id').tabs({configObject});Widget system simplifies the process of jQuery plugins development, providing additional ability to control the state of the code. At its core, this is another JavaScript objects system.jQuery UI widget factory is applied to add new widgets:1 2 3 4 5 6 7 | jQuery.widget("widgetNamespace.widgetName", { _create:function(){ }, doSomething:function(){ console.log("Hello World"); } }); |
$(‘#element’).widgetName({configObject})Namespaces are not used directly from the client code, but allow to avoid collisions with other jQuery plugins. Namespaces are used by jQuery as an array keys and can be viewed by looking up the global jQuery object in browser:console.log(jQuery.widgetNamespace.widgetName)What different components does it have (jQuery UI, jQuery events and so on)?
- jQuery UI
- jQuery event system
- Global AJAX event listeners
How does Magento use it?
Magento 2 strongly depends on the RequireJS library and its modules. Also, nearly all the Magento 2 JavaScript code is initialized with RequireJS modules. This is also applied to jQuery widgets, which can be created in the following way:lib/web/mage/list.js1 2 3 4 5 6 7 8 9 10 11 | define([ "jquery", 'mage/template', "jquery/ui" ], function($, mageTemplate){ "use strict"; $.widget('mage.list', {/*...*/}); /*...*/ return $.mage.list; }) |
1 2 3 4 5 6 | require([ 'jquery', 'mage/list' ], function($, list){ $('#element).list({configObject}); }) |
will be fully equivalent to the one we mentioned above.This works because RequireJS module, accountable for our widget creation, returns the widget instance after it was created, allowing to use data-mage-init and x-magento-init constructions.On the other hand, this method of widgets calling complicates their modification. In another system, this would not be a complicated task:1 2 3 4 5 | jQuery.widget('widgetNamespace.widgetName', {widgetDefinition}); /* ... */ jQuery.widget('widgetNamespace.widgetName', jquery.widgetNamespace.widgetName, newDefinition}); |
1 2 3 4 5 6 7 8 9 | var config = { "config": { "mixins": { "mage/dropdown": { 'Vendor_Module/js/dropdownMixin':true } } } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | define(['jquery'], function(jQuery){ return function(originalWidget){ jQuery.widget( 'mage.dropdownDialog', jQuery['mage']['dropdownDialog'], { open:function(){ return this._super(); } }); return jQuery['mage']['dropdownDialog']; }; }); |
1.4 Demonstrate understanding of KnockoutJS
Describe key KnockoutJS concepts
Knockout is a client-side rendering library that implements the Model-View-ViewModel pattern. The main feature of the library is an automatic UI refresh when the data changes.Describe the architecture of the Knockout library: MVVC concept, observables, bindings
Model-View-ViewModel (MVVC for short) is a software architectural pattern that serves to separate the graphical user interface development from the backend development. MVVC contains 3 components:- Model contains data,
- View displays data on the screen,
- ViewModel is an intermediary between Model and View. It allows to receive data for the View and influence the Model.
Bindings
The Knockout library also has a number of bindings that facilitate various interactions between data and elements. Bindings are specified as an attribute of the data-bind html element or as an html comment:1 2 3 4 5 6 7 8 9 | <select data-bind=" options: availableCountries, optionsText: 'countryName', value: selectedCountry, optionsCaption: 'Choose...'"></select> <!-- ko if: isVisible--> <span>Visible</span> <!-- /ko --> |
List of bindings:
Appearance:- visible – show / hide element depending on the condition
- text – display text and html tags as text
- html – display html and render html tags
- css – add css class depending on the condition
- style – add css style depending on the condition
- attr – set attribute values
- foreach – duplicate the element content for each item in the specified array
- if – add the condition block contents to the Document Object Model (later DOM) if they are true
- ifnot – add the condition block contents to the DOM if they are false
- with – create a new bundling property context in which you can directly address the sub-properties of a certain property.
- component – embed an external component in the DOM
- click – function call when clicked
- event – function call when a certain event occurs
- submit – function call when submit
- enable – enable the element when the condition is true
- disable – disable the element when the condition is true
- value – change the value of the form field
- textInput – similar to value, it works with text fields only and provides for all types of user input, including autocomplete, drag-and-drop, and clipboard events
- hasFocus – change the value of the property when it is received (into true) and when the focus is lost (into false)
- checked – change property value when selecting checkbox or radio elements
- options – provide options for dropdown
- selectedOptions – provide selected options from the dropdown
- uniqueName – set a unique name for form fields with an empty name
- template – render another template
- i18n – perform a string translation into the current language of the site
Observables
Observables are JavaScript objects that serve to notify subscribers about changes and automatically detect dependencies. Knockout Observables allow to change the values of variables and automatically notify all the subscribers by sending an event.Example:html:The name is script:1 2 3 4 5 6 7 | var myViewModel = { name: ko.observable('Bob'), change: function() { this.name('Alice'); } }; ko.applyBindings(myViewModel); |
Demonstrate understanding of knockout templates
Demonstrate understanding of knockout templates
Knockout uses the data-bind attribute and HTML comments to calculate expressions. Additionally, Magento provides an alternative syntax for Knockout templates that can be found in the official Magento documentation: https://devdocs.magento.com/guides/v2.3/ui_comp_guide/concepts/magento-bindings.htmlWhat are the pros and cons of knockout templates?
Benefits:- Support for older browsers up to IE 6
- Simple library
- Separating HTML and JavaScript
- Dynamic data binding
- Poor performance when the objects are numerous
- Since Magento switches to PWA and uses React, there is a probability that Knockout will sooner or later become deprecated.
Compare knockout templates with underscore JavaScript templates
Underscore template is rendered only when calling the _.template(template)(params) method, while the Knockout template is automatically re-rendered if data changes.Underscore uses <%= … %>, <% … %>, <%- … %> blocks to execute scripts; Knockout, at the same time, uses the data-bind attribute in html elements and html comments.Demonstrate understanding of the knockout-es5 library
Knockout observables are modified the same way as functions and their values are obtained similarly:1 2 | var value = this.knockoutProperty(); // get this.knockoutProperty(value); // set |
1 2 | var value = this.knockoutProperty; // get this.knockoutProperty = value; // set |
this.knockoutProperty += value; // setwhich is identical to the following expression:this.knockoutProperty(this.knockoutProperty() + value)In order to apply a plugin to the model, address the ko.track(someModel).Additionally, as the second parameter, it is possible to specify an array of model fields to limit the fields this plugin is applied to:ko.track(someModel, ['firstName', 'lastName', 'email']);Now to get the original observable, you need to callko.getObservable(someModel, 'email')The plugin also allows you to simplify the value of the calculated functions.For example, in order to writeInstead ofone needs to call the following code:ko.defineProperty(someModel, 'subtotal', function() {return this.price * this.quantity; });