4

I'm trying to add a message in the minicart, but:

  • The message depends on some external API calls I have to make at page load
  • I can't figure out how to pass data to my UI component when it's inserted within the jsLayout argument of a block in a page layout

Note that I'm new to Magento, but I've made a lot of research and can't figure out what I need to do.
Hopefully I haven't missed anything obvious...

What works

I managed to get a "static" message at the correct position in the minicart by doing the following:

/view/frontend/layout/default.xml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="minicart">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="minicart_content" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="extra_info" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="minicart.message" xsi:type="array">
                                            <item name="component" xsi:type="string">Module/js/view/checkout/minicart/message</item>
                                            <item name="config" xsi:type="array">
                                                <item name="template" xsi:type="string">Module/checkout/minicart/message</item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

/view/frontend/web/js/view/checkout/minicart/message.js

define(
    [
        'uiComponent',
    ],
    function (
        Component,
    ) {
        'use strict';

        /** Add view logic here if needed */
        return Component.extend({
            initialize: function() {
                this._super();
                this.message = 'OKAY';
            }
        });
    }
);

/view/frontend/web/template/checkout/minicart/message.html

<div>
    <span data-bind="text: message"></span>
</div>

"OKAY" displays correctly below the cart totals:

Message displays in minicart

What I'm looking for

I'd like message to be dependent on some API calls I have to make from the backend.

From what I understand, I could do that by using a DataProvider.

I have created the following DataProvider:

namespace Module\Model\Ui\MiniCart\Message;

use Magento\Ui\DataProvider\AbstractDataProvider;

class DataProvider extends AbstractDataProvider
{
    public function getData()
    {
        return ['data' => ['message' => 'My dynamic message']];
    }
}

And that's basically where I'm stuck. The dev docs linked above mention implementing the getDataSourceData() in the "UI component’s PHP class" to return $this->getContext()->getDataProvider()->getData().

All of this implies that my UI component has been defined within <Module>/view/frontend/ui_component/message.xml and included in the page layout using <uiComponent name="message"/>, which is not the case.

I have tried creating the ui_component/message.xml to add a component definition in there, but it's not being picked up (I checked by adding an invalid <invalidTag/> in it, which did not throw any error).

I have tried adding <item> tags to my default.xml layout:

<item name="minicart.message" xsi:type="array">
    <item name="component" xsi:type="string">Module/js/view/checkout/minicart/message</item>
    <item name="provider" xsi:type="string">Module\Model\Ui\MiniCart\Message\DataProvider</item>
    <item name="config" xsi:type="array">
        <item name="template" xsi:type="string">Module/checkout/minicart/message</item>
    </item>
</item>

But this doesn't work either, as I guess within the jsLayout definition Magento doesn't pick up any PHP object anymore and the data is just passed on to the UI component directly (?).

I also tried to provide a more "generic" component name such as Module/message and no other config, in the hope Magento would try to resolved the component via the ui_component/message.xml, but to no avail.

I'm obviously lost here and I'm left wondering, how can I:

  • Make a call to an external API
  • Decide which message should be displayed based on API response
  • Display this message in the minicart

Thanks for your help!

1 Answer 1

6

To anyone coming here with the same question, here's how I've done it in the end.

I found no way to solve what I thought was the correct solution, using a DataProvider, instead I injected my message into the cart "section data" that is refreshed from the frontend whenever there's an action done in the cart (updating quantities, adding/removing a product, ...).

Here's the setup:

The frontend part

/view/frontend/layout/default.xml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="minicart">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="minicart_content" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="extra_info" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="minicart.message" xsi:type="array">
                                            <item name="component" xsi:type="string">Module/js/view/checkout/minicart/message</item>
                                            <item name="config" xsi:type="array">
                                                <item name="template" xsi:type="string">Module/checkout/minicart/message</item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

/view/frontend/web/js/view/checkout/minicart/message.js

define(
    [
        'uiComponent',
        'ko',
        'Magento_Customer/js/customer-data',
    ],
    function (
        Component,
        ko,
        customerData,
    ) {
        'use strict';

        // Get cart section data
        var cartObservable = customerData.get('cart');

        return Component.extend({
            initialize: function() {
                this._super();

                // Make message an observable property
                this.message = ko.observable(cartObservable().message);

                // Subscribe to changes from cart section data
                cartObservable.subscribe((function(newCart) {
                    // Update message when cart section data changes
                    this.message(newCart.message);
                }).bind(this));
            },
        });
    }
);

/view/frontend/web/template/checkout/minicart/message.html

<div>
    <span data-bind="text: message"></span>
</div>

So what I'm doing here is injecting my JS component into the minicart, and the component gets and listens to changes from the cart section data.
This section data is updated whenever the cart changes.

What remains is: injecting my message into the cart section data.

The backend part

/etc/frontend/di.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    ...
    <type name="Magento\Checkout\CustomerData\Cart">
        <plugin name="cart_message" type="Vendor\Module\Plugin\Checkout\CustomerData\Message"/>
    </type>
    ...
</config>

I'm using a Plugin to update the CustomerData\Cart object before it's sent back to the frontend:

/Plugin/Checkout/CustomerData/Message.php

class Message
{
    /**
     * Add dynamic message to cart section data
     *
     * @param \Magento\Checkout\CustomerData\Cart $subject
     * @param array $result
     * @return array
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function afterGetSectionData(\Magento\Checkout\CustomerData\Cart $subject, $result)
    {
        $result['message'] = 'My dynamic message';
        return $result;
    }
}

With this, your message is inserted into the minicart and updated whenever the minicart is refreshed, which gives you the opportunity to display a message that depends on the current content of the cart.

1
  • Thanks for sharing, just what I was looking for! Commented Oct 14, 2022 at 13:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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