Categories


Archives


Recent Posts


Categories


Magento 2: UiElement Modules Defaults and UiRegistry Async Module Fetching

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

Continuing in our informal series of quickies explaining the more magical defaults properties of Magento uiElement objects, today we’ll explore the modules default.

The modules default allows you to link a property in your new object with an object in Magento’s uiRegistry via the registry’s “async” feature.

If that didn’t make sense, consider the following program. You can run this from your browser’s javascript console on the admin’s customer listing page (Customers -> All Customers)

//normally we'd pull in RequireJS module via `define`
reg = requirejs('uiRegistry');
UiElement = requirejs('uiElement');

//make sure the registry has a key of customer_listing.customer_listing
//if not, you're not on the All Customers page or Magento's changed
//something since this was written
console.log ( reg.get('customer_listing.customer_listing') );

//create our new class/constructor-function with a 
//modules default set
OurConstructorFunction = UiElement.extend({
    defaults:{
        modules:{
            foo:'customer_listing.customer_listing'
        }
    }
});    

//instantiate a new object
object = new OurConstructorFunction;

//call the now defined "foo" method to fetch the 
//customer_listing.customer_listing registry entry
console.log( object.foo() );

//test that its the same object
console.log( object.foo() === reg.get('customer_listing.customer_listing'));

As you can see from the above program, the

defaults:{
    modules:{
        foo:'customer_listing.customer_listing'
    }
}   

configuration automatically gave our instantiated objects a foo method which returns the customer_listing.customer_listing object from the registry.

The async Method

All these “registry getter” methods are setup via the uiRegistry object’s async method.

console.log(object.foo);
function async(name, registry, method) {
    var args = _.toArray(arguments).slice(3);

    if (_.isString(method)) {
    //...

You can see the entire method definition here

//File: vendor/magento/module-ui/view/base/web/js/lib/registry/registry.js

//the method definition
async: function (query) {
    return async.bind(null, query, this);
},

//the async function that's bound with `bind` in the method definition
function async(name, registry, method) {
    var args = _.toArray(arguments).slice(3);

    if (_.isString(method)) {
        registry.get(name, function (component) {
            component[method].apply(component, args);
        });
    } else if (_.isFunction(method)) {
        registry.get(name, method);
    } else if (!args.length) {
        return registry.get(name);
    }
}

The uiElement source binds the foo property in the code below, where name is foo (or whatever property you use in your modules default).

#File: vendor/magento/module-ui/view/base/web/js/lib/core/element/element.js

requestModule: function (name) {
    var requested = this._requesetd;

    if (!requested[name]) {
        requested[name] = registry.async(name);
    }

    return requested[name];
}    

The async method lets you setup a callback that fetches a registry object – even before that registry object is set. Consider the following program

//normally we'd pull in RequireJS module via `define`
reg = requirejs('uiRegistry');
UiElement = requirejs('uiElement');

//create our new class/constructor-function with a 
//modules default set for the ourUniqueRegistryName
//registry key
OurConstructorFunction = UiElement.extend({
    defaults:{
        modules:{
            bar:'ourUniqueRegistryName'
        }
    }
}); 

//instantiate a new object
object = new OurConstructorFunction;

//use the bar method/callback now and it returns nothing
console.log( object.bar() );

//however, set something in the registry with
//the ourUniqueRegistryName key from the modules default
reg.set('ourUniqueRegistryName', 'something');   

//and now you can fetch the item
console.log( object.bar() );

If you follow along with the comments above, you can see how this async() function enables us to link in a registry object even if that object isn’t yet registered.

The new modules default method also behaves differently depending on the arguments you pass it. For example, dovetailing off our last code sample

//define a simple object/method
var test = {
    hello:function(noun){
        console.log("Hello " + noun);
    }
}          

//re-register the ourUniqueRegistryName registry key
reg.set('ourUniqueRegistryName', test);   

//fetch the entire object by using no arguments
console.log( object.bar() );

//call the `hello` method by passing in the 
//string "hello" as the first argument, and
//then the following arguments will be passed
//on to the hello call    
object.bar('hello', "World");

This method calling seems like a bit of a bind parlor trick, as the following also works

object.bar().hello("World")

but as Magento occasionally uses the former syntax in its core systems code, you’ll want to be familiar with it.

Copyright © Alan Storm 1975 – 2017 All Rights Reserved

Originally Posted: 28th November 2016