Categories


Archives


Recent Posts


Categories


Using the uiRegistry Asynchronously and Safely

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.

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

If you’re following along with my UI Component and uiElement Internals series, you know about Magento’s uiRegistry module. You can use this module to fetch the scoped Knockout.js view models by registered name

requirejs(['uiRegistry'], function(reg){
    console.log(
        reg.get('customer')
    );
});

or by using a callback function to see every registered view model.

requirejs(['uiRegistry'], function(reg){
    reg.get(function(item){
        console.log(item);
    });
});    

While useful as a debugging tool, using the uiRegistry with real Magento modules presents a problem – you can never be certain when a module is available due to RequireJS’s asynchronous module loading. While asynchronous loading is great for performance, and for developers able to work within their own self contained non-global state – on the ground web developers often need access to data that’s only accessible via a registered view model.

The problem is, there’s no way to guarantee that

reg.get('customer')

will always return a the registered customer view model. Depending on network latency, its possible your own RequireJS module may run before the customer view model is available.

Fortunately, the uiRegistry’s get method features its own asynchronous loading features. Consider the following program

requirejs(['uiRegistry'], function(reg){
    console.log("Starting the Program");

    console.log("Requesting the item with the registry index of our_registered_item");
    reg.get('our_registered_item', function(item){
        console.log("We Just Called the Callback");
        console.log(item);
    });

    console.log("Adding The Item");
    var object = {foo:"bar"}        
    reg.set('our_registered_item', object);
});      

Run the above program in your javascript console, and you’ll see the following output

Starting the Program
Requesting the item with the registry index of our_registered_item
Adding The Item

We Just Called the Callback
Object {foo: "bar"}

By using the get method with a callback

reg.get('our_registered_item', function(item){
    console.log("We Just Called the Callback");
    console.log(item);
});    

We told the uiRegistry

Hey, once the our_registered_item object is available, run our function.

Since our_registered_item wasn’t available immediately, our program kept running. The callback didn’t fire until our_registered_item was actually registered.

If you need more than a single item, this asynrcoutous functionality works with multiple references. Consider the following

requirejs(['uiRegistry'], function(reg){
    console.log("Starting the Program");

    console.log("Requesting the item with the registry index of our_registered_item");
    reg.get(['our_registered_item_1','our_registered_item_2'], function(item1, item2){
        console.log("We Just Called the Callback");
        console.log(item1);
        console.log(item2);
    });

    console.log("Adding The Item");
    var object = {foo:"bar"}        
    reg.set('our_registered_item_1', object);

    var object = {baz:"ban"}        
    reg.set('our_registered_item_2', object);        
});     

Here we passed get an array of registry keys

reg.get(['our_registered_item_1','our_registered_item_2'], function(item1, item2){
    console.log("We Just Called the Callback");
    console.log(item1);
    console.log(item2);
});   

Again, our callback was not called until both our_registered_item_1 and our_registered_item_2 were available in the registry.

If you’re using the uiRegistry in your own program, you must use this sort of pattern for fetching items. While the code may work in your development enviornment without it, network latency from the production servers means you can never be sure when an item is available. RequireJS module that use the uiRegistry should all look something like this

requirejs(['uiRegistry'], function(reg){
    /* ... */
    reg.get([/* list of registry entries */], function(item1, item2, etc.){
        //program logic here
    });
    /* ... */        
});

Copyright © Alan Storm 1975 – 2019 All Rights Reserved

Originally Posted: 13th December 2016