21

enter image description here

what does displayArea and provider of checkout_index_index.xml mean in magento2?

21

To understand what checkoutProvider and displayArea are, you must first understand the scope you are looking in: jsLayout.

jsLayout is a bunch of JavaScript configuration for the JavaScript UI elements on the checkout page. If you look at module-checkout/view/frontend/templates/onepage.phtml, you'll notice the following x-magento-init-data:

<script type="text/x-magento-init">
    {
        "#checkout": {
            "Magento_Ui/js/core/app": <?php /* @escapeNotVerified */ echo $block->getJsLayout();?>
        }
    }
</script>

This is where it all starts. It states:

For element #checkout, initialize the Magento_Ui/js/core/app-component with the following information: ...

And the information it receives is the information created in the layout XML: jsLayout. Now, this means that everything in your XML is now passed through to the Magento_Ui/js/core/app-component (leaving plugins, and layout processors and stuff out of the equation for the moment...)

Now, I'm not going to dive into detail into how module-ui/view/base/web/js/core/app.js boils everything down, because that would make this post very, very long, but the summary is this:

  • The Magento_Ui/js/core/app-component creates a checkout-component.
  • This will be a component of the type uiComponent (this is a very generic component that can be used to defer your own custom UI components from. It comes with basic knockout template rendering and stuff).
  • It will us the template Magento_Checkout/web/frontend/template/onepage.html.
  • It will create various children (with the name errors, estimation, steps, etc...)
  • The steps-child will also be a uiComponent.
  • This cycle continues... the configuration makes children with various parameters.

Now to get to your displayArea and provider-question: As you've seen above, everything maps to JavaScrip classes. The first time we see the use of displayArea is when we create the steps-component, which is of the type uiComponent. So uiComponent would be a logical candidate to look for the use of displayArea.

Now, a uiComponent is a JavaScript class of the type Magento_Ui/js/lib/core/collection. (You can look this up in module-ui/view/base/requirejs-config.js). This maps to module-ui/view/base/web/js/lib/core/collection.js. Here we see the following use:

/**
 * Synchronizes multiple elements arrays with a core '_elems' container.
 * Performs elemets grouping by theirs 'displayArea' property.
 * @private
 *
 * @returns {Collection} Chainable.
 */
_updateCollection: function () {
    var _elems = compact(this._elems),
        grouped;

    grouped = _elems.filter(function (elem) {
        return elem.displayArea && _.isString(elem.displayArea);
    });
    grouped = _.groupBy(grouped, 'displayArea');

    _.each(grouped, this.updateRegion, this);

    this.elems(_elems);

    return this;
},

So what this does in effect, it 'maps' a uiComponent to a certain group of UI components. This is important to know, because it allows us to move UI components to other locations in the layout, by just manipulating the XML layout, just like you would do this with phtml templates that are rendered server-side. Just override the displayArea, and you can render any JavaScript UI Component anywhere else (given that the target area is also rendered somewhere).

Now for your second question: provider. Just like we've looked up displayArea, we should start looking at the UI Component first, which is Magento_Checkout/js/view/form/element/email. And if we look at the requirejs-config.js, we finally find module-checkout/view/frontend/web/js/view/form/element/email.js.

But ... no provider is used in this class. So let's just see if we can find anything in the class it extends: Component (which is our uiComponent-class again).

But ... no provider as well. Well, uiComponent simply extends Element (which is located at module-ui/view/base/web/js/lib/core/element/element.js), so let's just look over there:

/**
 * Parses 'modules' object and creates
 * async wrappers for specified components.
 *
 * @returns {Element} Chainable.
 */
initModules: function () {
    _.each(this.modules, function (name, property) {
        if (name) {
            this[property] = this.requestModule(name);
        }
    }, this);

    if (!_.isFunction(this.source)) {
        this.source = registry.get(this.provider);
    }

    return this;
},

Bingo! It turns out that the provider is used as a source to fetch data from. If we look at the constructor of Element, you'll see that by default, it's set to empty:

provider: '',

So back to our configuration. If we now read our configuration, we'll understand that the item shippingAddress is a component of Magento_Checkout/js/view/shipping, that fetches it's data from the checkoutProvider.

So that leaves us with two questions:

  1. Where is checkoutProvider defined?
  2. How is it used in the shipping JavaScript?

Well, if you'll scroll to the bottom of checkout_index_index.xml, you'll notice that it's nothing more than a vanilla uiComponent:

<item name="checkoutProvider" xsi:type="array">
    <item name="component" xsi:type="string">uiComponent</item>
</item>

And if you look at module-checkout/view/frontend/web/js/view/shipping.js, you'll see that it is used like this:

registry.async('checkoutProvider')(function (checkoutProvider) {
    var shippingAddressData = checkoutData.getShippingAddressFromData();

    if (shippingAddressData) {
        checkoutProvider.set(
            'shippingAddress',
            $.extend({}, checkoutProvider.get('shippingAddress'), shippingAddressData)
        );
    }
    checkoutProvider.on('shippingAddress', function (shippingAddressData) {
        checkoutData.setShippingAddressFromData(shippingAddressData);
    });
});

To be honest: this is where my analyzing stops, because it becomes for me also hard to search and invest what's going on, but I hope someone else can pick it up from here ...

I know it has something to do with the registry.async() returning a method that gets immediately executed with a callback function as argument, but someone else needs to explain this...


* Disclaimer: By all means, please correct me if I'm wrong! Haven't tried any of the above for real, but I have been working almost for year now with Magento 2 and I believe this is how it works. Unfortunately there isn't much documentation if you want to dive to the bottom of the Magento Ocean.

  • 1
    so what is displayArea ? – Marián Zeke Šedaj Nov 28 '16 at 16:52
  • 1
    This is brilliant analysis, did you ever develop a further understanding? – LM_Fielding Jun 21 '17 at 9:12
11

6 months after my original answer I think I can provide a better answer on what displayArea is.

In my understanding, it all comes together with Knockouts' getTemplate()-method, the getRegion()-method, and children in UI Components. A good example of this can be seen when you're examining vendor/magento/module-checkout/view/frontend/templates/registration.phtml and vendor/magento/module-checkout/view/frontend/web/template/registration.html.

In registration.phtml, you'll see a default Magento UI Component that has children:

<script type="text/x-magento-init">
    {
        "#registration": {
            "Magento_Ui/js/core/app": {
               "components": {
                    "registration": {
                        "component": "Magento_Checkout/js/view/registration",
                        "config": {
                            "registrationUrl": "<?php /* @escapeNotVerified */ echo $block->getCreateAccountUrl(); ?>",
                            "email": "<?php /* @escapeNotVerified */ echo $block->getEmailAddress(); ?>"
                        },
                        "children": {
                            "errors": {
                                "component": "Magento_Ui/js/view/messages",
                                "sortOrder": 0,
                                "displayArea": "messages",
                                "config": {
                                    "autoHideTimeOut": -1
                                 }
                            }
                        }
                    }
                }
            }
        }
    }
</script>

Note the use of displayArea in the children-node. Basically, it tells Knockout that this child element should be rendered in a region called 'messages'.

Now take a look at the top of registration.html:

<!-- ko foreach: getRegion('messages') -->
    <!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko-->

What this line of Knockout code basically does, is: it iterates over all child elements that are present in the displayArea 'messages', and renders them.

Basically, the naming is a bit confusing if you ask me. Why would you use 'displayArea' in one place, and 'region' in another place. But perhaps my assumption is totally incorrect. Perhaps a Magento core developer could shine a bit more light on this?

  • 1
    This is what's confused me for so long, I keep seeing getRegion and my mind just implodes. Thank you for both answers by the way, very helpful! – Ben Crook May 18 '17 at 13:43
  • 1
    Well, this is just my 2 cents. I hope somebody of the core developers can share some light on this topic. The deeper internals of Magento 2, and especially the whole Knockout / XHR implementation are something that has been not so-well documented. – Giel Berkers May 18 '17 at 14:38
  • 2
    Agreed, unless you delve into many of the core files there's pretty much no way other than this stack exchange to know what the hell is going on. – Ben Crook May 18 '17 at 14:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.