Widgets
About
Widget — is the main interface to create JS Component. Breeze widgets are very similar to Luma’s jQuery UI based widgets. In most cases you can even reuse the same file for both Luma and Breeze Themes
$.widget function
$.widget
— is a function that is usually used to declare a new widget:
$.widget('uniqueName', {});
You can also retrieve all widget instances using this function:
// iterate over all instances
$.widget('uniqueName').each(function (widget) {
console.log(widget);
});
// call for specified method in all instances
$.widget('uniqueName').invoke('close');
// destroy all instances
$.widget('uniqueName').destroy();
Declaration
There are three main parts in widget declaration:
$.widget
function call- Widget name — The name is used for event names and to work with widget programmatically.
- Object with methods and properties — A prototype to use when creating widget instance.
$.widget('uniqueName', {
component: 'Vendor_Module/js/component'
});
Sometimes you’ll want to create a widget as a child of some built-in widget. In this case parent name should be added to the declaration:
You must also add
import
section to yourbreeze_default.xml
file.
define(['Magento_Ui/js/modal/modal'], (parentWidget) => {
'use strict';
$.widget('uniqueName', parentWidget, {
component: 'Vendor_Module/js/component'
});
});
Prototype object is usually consist from:
component
string — Component alias to search in DOM structure. Used to automatically initialize component. View details about Component Initialization.options
object — Default component options.create
method — Main entry point where you want to place component’s logic.destroy
method — A place to undo all work made during component’s lifecycle.
Example
Let’s start with full-featured widget declaration that uses most of available built-in properties and methods, and then review each peace of code.
$.widget('uniqueName', {
component: 'Vendor_Module/js/component',
options: {
activeClass: 'shown',
template: '...'
},
create: function () {
this.popup = $(this.options.template).appendTo(document.body);
this.focusTrap = this.createFocusTrap(this.popup);
this._on('click', this.open);
this._on(this.popup, {
'click .close': this.close
});
},
destroy: function () {
this._super();
this.popup.remove();
},
open: function () {
this.popup.one('transitionend', this.focusTrap.activate);
this.popup.addClass(this.options.activeClass);
this._trigger('opened');
},
close: function () {
this.focusTrap.deactivate();
this.popup.removeClass(this.options.activeClass);
this._trigger('closed');
}
});
Properties
component
The component
property makes your component automatically mounted during
page load. The value of this property is the alias of your component in the DOM
structure.
For example, when two components are declared in HTML markup as follows:
<div data-mage-init='{"Vendor_Module/js/component": {}}'></div>
<div data-mage-init='{"dropdown": {}}'></div>
You should use the following corresponding values for component
property:
component: 'Vendor_Module/js/component'
component: 'dropdown'
options
Use options
object to declare default settings for your component:
options: {
activeClass: 'shown',
template: '...',
nested: {
key: value
}
}
You can access these options inside your component directly (this.options.activeClass
)
or using _option
method:
this._option('nested/key', 'defaultValue');
Methods
create
Component constructor. this.element
and this.options
are ready to use in this method.
create: function () {
this.popup = $(this.options.template).appendTo(document.body);
this._on('click', this.open);
}
init
Component initialization method. It’s called once after create
method,
and every time after constructor call on already initialized widget instance:
// 1. create widget on element
$(el).widgetName();
// 2. call once again on the same element:
$(el).widgetName({});
On the example above, create
method will be called once only, but init
will be called twice.
destroy
Component destructor. It’s called before the page unload event when
Turbo mode is enabled.
Since base Widget class has its own destroy logic you should call this._super()
inside your destroy method.
destroy: function () {
this._super();
this.popup.remove();
}
Parent destructor automatically removes all event listeners added via _on
method,
and deactivates focusTrap
if it was used by the component.
createFocusTrap
Useful when you need to lock the customer’s focus inside popup or slideout panel. This method creates a focus-trap instance that you need to activate separately to lock the customer within specified DOM node.
// create focus-trap instance
this.focusTrap = this.createFocusTrap(this.popup);
// activate focus-trap
this.focusTrap.activate();
// deactivate focus-trap
this.focusTrap.deactivate();
Please note, when trap element is animated, you should activate
focus-trap
after the element was animated:this.popup.one('transitionend', this.focusTrap.activate);
onReveal
A method that accepts element and callback to execute them this element enters the
visible area. You can skip the element parameter. In that case this.element
will
be used:
this.onReveal(() => console.log('Hello!'));
this.onReveal($('.selector'), () => console.log('Hello!'));
Please note, this method runs only once per visit.
_option
A method to get the value of specified option. Useful when option is nested deeply
and you want to get default value if option is not set. In the example below,
defaulValue
will be returned if this.options.nested.key
is not exist or
is undefined
:
this._option('nested/key', 'defaultValue');
_on
Attach event listeners to this.element
or any other element. Prefer to use this
method because:
- All listeners added via
_on
will be automatically removed indestroy
method. - Breeze will bind
this
to the handler function automatically.
Listen to clicks on this.element
and call this.open
after each click:
this._on('click', this.open);
Listen to clicks on all elements with close
class name inside this.popup
element:
this._on(this.popup, {
'click .close': this.close
});
_off
Remove event listeners from this.element
or any other specified element:
this._off('click'); // remove click listeners from this.element
this._off(); // remove all event listeners from this.element
this._off(this.popup, 'click');
_trigger
Manually dispatch specified event. Component namespace is added to the actual dispatched event:
this._trigger('opened');
And here is how you should add event listener for this event:
$(el).on('widgetName:opened', function (event, data) {
console.log(data.instance);
});
Events
beforeCreate
The beforeCreate
event is dispatched right before this.create
method is called.
Usage example:
$(el).on('widgetName:beforeCreate', function (event, data) {
console.log(data.instance);
// we can do any additional logic here
// or we can even change options inside data.instance!
});
afterCreate
The afterCreate
event is dispatched right after this.create
method is called.
Usage example:
$(el).on('widgetName:afterCreate', function (event, data) {
data.instance.open();
});