Pass our own server data to the Minicart

From the previous post how server side data is being passed to the Minicart, you remember that Magento uses window.checkout object to configure the original minicart.js component. And I had an idea to modify the window.checkout object inside minicart.phtml, which seems rather straightforward and nice.

While this is all practical, lets try to figure out a way to pass data to our component in a way that the value is actually a property of the component.

If you are not the type that likes too much experiments, I promise, this is that type of article that has high chances of giving a headache, so, now is the moment to go back to your favorite series :). For the rest of you, lets start playing around with Magento by opening

https://gist.github.com/marjan7790/d6331447c83d420014d22c4a05a30ca2

and take a look at this code more carefully

https://gist.github.com/marjan7790/36fe1702a71ab2bc710eeea5c57ac983

In essence, this says:

Pass a JSON object to Magento_Ui/js/core/app

Practically, Magento_Ui/js/core/app is alias to app.js, which is found in vendor/magento/module-ui/view/base/web/js/core/app.js, and serves to create the UI components instances according to the configuration of the JSON using uiLayout.
(original text from: https://devdocs.magento.com/guides/v2.2/ui_comp_guide/concepts/ui_comp_config_flow_concept.html)

Next, lets see where this $block->getJsLayout() code comes from.
Well, at the top of the minicart.phtml, we can see

https://gist.github.com/marjan7790/8986394570f50f45ed01c1965535ffc0

but in the mentioned class there is no getJsLayout() method. Tracking through inheritance, gives us this chain

https://gist.github.com/marjan7790/05091cc53bd9d7621fd4615f8ad5c587

and inside the AbstractBlock we can see

https://gist.github.com/marjan7790/c568048be3528a3f3968b1f8593f543e

If we were to setup a break point over here, we will see that $this->jsLayout is a mere PHP array that will be turned into json object, the content of which looks like

p2

OK, since app.js serves to create instances according to configuration, and there is “config” key in this array, lets try to add data to the config.

Go ahead and create etc/di.xml with the following content

https://gist.github.com/marjan7790/69107f04517ec70acee169db6a5a1f30

and inside the Block/Cart folder, create the class Sidebar.js with

https://gist.github.com/marjan7790/3c096f592258023039d36165f8139772

If effect, I’ve set new key named maxItems with the config sub-array of the minicart_content component.

You probably need to re-compile so the server end actually considers using your block instead of the original, and you might need to re deploy the static content to get the .js changes.

Eventually, you will re-run the page and right click on the browser and “View Page Source”.  Search within the source for

[data-block='minicart']

and you will see what gets send to Magento_Ui/js/core/app.

I’ve copied the JSON that is passed and send it to https://jsoneditoronline.org/, so I have a nice tree like representation which helps visualize things. Thus, the JSON object looks like

p3

Great, the new key is there. But, how do we use it?

Replacing in minicart.js,
maxItemsToDisplay: 3

with
maxItemsToDisplay: origMiniCart.maxItems

does not work.

Well, if you make the code to be like

https://gist.github.com/marjan7790/65f89a8153dc04fdcc58c8e1a51d3550

and set a break point at var tmp = origMiniCart; you will see that that tmp is actually uiClass constructor, which in essence means we still do not actually have our object. Remember, from previous tutorial, the original initialize() has not executed yet.

If we don’t have an object, then, lets just create one! Update the code to be like

https://gist.github.com/marjan7790/6899d704c38328dee78a80cdfda94b17

Now, this works! But … why exactly?

It is important to know that you could send anything to your component in the config key and surely it will be available as property to your object. For example, lets say you have something like this in your .phtml

https://gist.github.com/marjan7790/acb101eb0e892e0f24df638dc653dbd9

With this code

  • You have declared that your component is named mycomponentname
  • have told that the actual js file is Vendor_YourModule/js/mycomponent
  • have told what the .html template is Vendor_YourModule/mycomponenttemplate
  • set a component title
  • added a new config property named valueToPass

If you have your component instantiated at some point you should be able to call
    mycomponentname.valueToPass
and this will return 55.

But, the origMiniCart is still merely a constructor function, so, this is exactly what we remedy against.

Lets dig in the original minicart.js initialize() function. It surely does first its own specific things, but eventually returns

https://gist.github.com/marjan7790/9874b3376536db681de15f53a33b41ad

Well, lets track this and figure who is the parent of this component?

It actually says return Component.extend({ …,

and if we scroll up in the “define” section of this RequireJs module, we will see that Component is alias for uiComponent and actually refers to Magento_Ui/js/lib/core/collection.

If you wonder where this alias comes from, open

https://gist.github.com/marjan7790/1acc044a4743851fa34f8be279a4b058

But anyways, opening Magento_Ui/js/lib/core/collection we see returns Element.extend({ and here Element is actually alias to uiElement, which (again from requirejs-config.js) is alias to Magento_Ui/js/lib/core/element/element.

Element is where for the first time we will see initialize() method that actually returns return this;, and not return this._super(); as wee saw in minicart.js.

So, uiElement is as far as the hierarchy goes. Therefore, if we typed in our initialize() something like

https://gist.github.com/marjan7790/7693e5aed1d9d053d6d46bbbe25a84a1

we will eventually force uiElement to return an actual object.

After that, its easier. We simple refer to the new property maxItems and assign it to the actual property that the original script uses, which is maxItemsToDisplay.

Finally, we return the parent object.

Check the 0.2.0 version of the module.


Conclusion

This article showed how to send new properties to a given component by passing customized json to app.js that added to the config array. Please keep in mind, that with this example, the new maxItems property is set to the original minicart component first. And this is all done in place after server side PHP has executed.

However, on the client end, at the moment of applying the mixins, I have instantiated an actual original minicart object, read from it the new property and updated an existing one. Not the most natural thing to do, but after this I hope you get a sense of what could potentially work as a way to pass server side data.

In the next article, we will continue to do an actual override of the component.

Thank you for reading.

Leave a comment