Using the local storage

OK, so lets the local browser storage in our component.

Edit the minicart.js to have a code like

https://gist.github.com/marjan7790/286d4e052b39200e9d646d352eb5c599

Re-deploy if you must and observe the Local Storage – it should have a new key there named cartExtra, just like on this picture

cartExtra-storage

Well, since you’ve come this far, congratulations, you are a very good developer, and I’m sure you do not need any further explanations about the code šŸ™‚

Feel free to checkout the source code from tag 0.4.0

Thank you for reading.

Passing customer data to the Minicart

In a previous article, How Magento passes server data to minicart.js, we saw how Magento will use the general window object to pass server data, and later in article Pass our own server data to theĀ Minicart we saw how to pass data via the <script type=”text/x-magento-init”>.

But, where exactly is the customer data, like, the cart items, coming from?

I will start looking at the original minicart.js, as set in the module-checkout and reffered to quite a lot so far. This is a good entry point

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

First to notice, var items is acquired from this.getCartParam() function which is defined as

https://gist.github.com/marjan7790/635c97c21314894066f21cc5df123e0b

which means that there is internal property named cart, that among other keys, contains the items key as well.

Indeed, we can see this is a “class level” attribute and is defined as an empty object like cart: {}.

Lets take a look at initialize() again, specifically at

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

And then this leads to the update() function

https://gist.github.com/marjan7790/6323a8ef5cc7dec662a637be46fa2f88

OK, it is now clear that the update() function does add keys and assigns values to the this.cart, and that as an input parameter it uses: customerData.get(‘cart’);

And what is customerData? Well, lets look at the define section of this RequireJs module and notice that this is another module, defined originally in Magento_Customer/js/customer-data and loaded in this minicart.js

What this means? This minicart.js is dependent on another module, and that module on its own is responsible to provide the data. The minicart.js methods are merely reading this and re-distributing in its own properties (this.cart) in the way that will be most convenient to do the display of the data.

So, we are coming to the an important notice here: minicart.js is within the web/js/view folder of the module-checkout. Please notice that there are /web/js/action and web/js/model folders as well.
This all means, there are .js modules that will be focusing on modeling (defining the essence of the js object) or taking actions only (like collecting and submitting data from and to the server), and sure enough, there are .js modules, just like our minicart.js that will be used for the rendering part of this whole idea.

You see, the web/js folder is now a complicated mini JS application in itself. Slightly of topic here, but each Magento module, may have its own JS application embedded with its /web folder. Just keep this in mind. This well may be the future of the Web, not just Magento: any reusable headless back-end component (module, bundle) may come with at least one default front-end implementation, i.e. the default head.

Anyway, the question of how the minicart.js gets its data from the server, has now transposed into how Magento_Customer/js/customer-data gets its data?

Open

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

First thing to notice this js file returns a js object like return customerData;

Containing other function and objects as well as few plain old listeners ($(document).on()), the essence of this file is to produce and return the customerData variable, which means whoever claims that they depend on the customer-data.js, they will get this customerData object.

So, what to focus to? Its not easy to know what to look at, so it is always a good practice to set a couple of break points here and there and see when they get to be executed in the js stack.

But, by habit almost, lets start at the init() function. I lead you to focus on this code

https://gist.github.com/marjan7790/43e9be18d9fb25f37983c7578a7e8585

There are there different sections and the get their data from dataProvider.getFromStorage().

And dataProvider is defined in this same file, above the customerData object. Looking at the getFromStorage definition we see

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

The last ingredient here is to notice that storage is defined even further above in this file like:

https://gist.github.com/marjan7790/8db46baca3599a9a004928fd8ec75b38

Suffice it to say the browser’s local storage is a mechanism for storing data on a browser level and applications can use this for storing variables that are going to be used between two requests (remember HTTP being stateless?). For more info, please google and find more suitable articles.

Anyway, Magento has set its own storage key named ‘mage-cache-storage‘ and you can observe this is you were to load any magento frontend page and the inspect then , in Chrome -> Application -> Local storage, or in Mozilla -> Storage -> Local Storage.

There are of course other keys, but lets focus on the ‘cart’ one. It looks something like

cart-from-storage

So, this is how some of the server data is available to the js scripts. Perhaps in another article in the future I will tackle the topic of how this cart section of the mage-cache-storage gets set in the first place, but you do not have to go far to see how this might be done. Search, within customer-data.js, the update() function and you should find this implementation

https://gist.github.com/marjan7790/5f34944dd438d5a294c022d098b57f95

I leave it up to you how all of this flows. For this you could set a debug point, perhaps before the _.each function. Remove an item from the cart and this code will be triggered, but regardless of how complicated it might look, updating the storage boils down to storage.set(your-section, your-section-data);

After this I hope you have a nicer understanding how data is passed from the server and used by the frontend JS scripts. And again, “scripts” here is a sort of diminishment, since, all of the front scripts, as pointed earlier are specializing and contributing to somewhat larger – they product front head that nicely correlates to the back-end.

In the last article of this series, lets write some simple code to use the local browser storage capacity ourselves.

Thank you for reading.

Override the Minicart

So far I have altered some of the behavior of the Minicart using mixins. This usually means adding more properties and behavior to an already present functionality. With mixins, the properties and behavior of two different objects are merged and you end up enhancing your object in a way that is not possible via classical inheritance principles.

This given, deciding how many items can be shown in a given moment is not mixing up properties, since, well, theĀ maxItemsToDisplay has always been there, I just updated its value. This is practically overriding and I’ve used mixins for it.

In this article here we will be overriding the minicart component. Lets get started.

I assume you are the 0.2.0 version of the program. If you have loaded perhaps the home page of your application (or any other page except the checkout) and have added one break-point at this line

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

you will see the data behind the component may look like

myobj-after-super-mixins

In the case of the screenshot, the component is: “Magento_Checkout/js/view/minicart”.

So, lets do a couple of file system changes. First, change view/frontend/requirejs-config.js content to

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

OK, so, this code practically says we do not need requirejs-config.js at all!

But, I thought you might want to keep the old code commented out just as a reminder to the mixins syntax, and if you would keep the file, well, it needs to have var config in it. An empty one, but, has to have it.

If you do not want to keep the old mixins config, simply remove the file.

Next, edit the minicart.js to look like

https://gist.github.com/marjan7790/2d6dd7bd0bfc37b2412e847766cf2754

In the define, a dependency is added to the original minicart and that compoenent is now internally here referred to as Minicart. We extend it and return it with return Minicart.extend({.

The rest of the code, you have noticed, especially the initialize() function, is exactly the same!

We can’t stop here. This component won’t be loaded. There is an .xml file that dictates the js loading. In our case the original file is

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

so, we need to override it. Create default.xml file inside view/frontend/layout in your module with the following content

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

Find that line where the compoent is specified and edit Vendor_Yourmodule accordingly to your module.

So, re-deploy static content, clear magento cache and browser cache and you are done!

Set a brake point just like earlier and notice that the minicart component name is not the default one!

As one extra thing, we can deploy our own .html template file. First create file content.html inside view/frontend/web/template/ folder of your extension with this content

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

This is basically the original file, but I have added one single line

https://gist.github.com/marjan7790/4b45b98905b1229997b4286b7ff24196

Since new .html file is used, make sure that the default.xml specifies your file instead of the default one. Do this over here

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

Once you are done, a mere magento cache flush should sufice it, and you should see the Maximum items to display 1 message at the top of the minicart container.

You can always check the code from the 0.3.0 tag for comparison.


Notes and Conclusion

Note:

<span text=”some-js-variable-here” /> that is added to the content.html is NOT available by default Knockout.

Something like

<span data-bind=”text: maxItemsToDisplay”>

is probably the normal Knockout syntax.

Magento has simply taken Knockout a step further adding one more layer of abstraction here. By not clearly listing all of these specifics, Magento does make these things one level harder for the community.

Anyway, we have now the mini-cart overridden.

As a summary, to achieve this
– we don’t need to create requirejs-config.js at all to override a js component
– we do need to override a native layout .xml file
– we could specify our own .html template

Addressing finally the elephant in the room: initialize() is completely the same both in using the mixins and the override approach. Well, we are anyway returning some-object.extend(), and it is the same object, we merely use different references for it. And in both cases our code executes before the original initialize() does. So, naturally, the code stays the same.

This last one can be very instructive: if the code from version 0.3.0 is claimed to be a better solution to the requirements, it is only because it satisfied and acknowledges the well established classical idea/concept of overriding. It does not work better, and you’ve already tackled a default.xml file which you did not had to do it 0.2.0.

Thus, almost always ask yourself: is an established concept always worth applying?

I’d hate to leave you with such philosophical questions, so, lets continue and extend this series and take a look at the question: where the data is really coming from.