We all know that recently viewed products are easy to show anywhere on a Magento 2 website. There are many ways to show recently viewed products. The easiest way would be the widget way. For example, if you add the following content in your site
File: `app/code/[NameSpace]/[Module]/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>
<referenceContainer name="footer-container">
<block class="Magento\Catalog\Block\Widget\RecentlyViewed"
name="footer_recently_viewed_widget"
template="Magento_Catalog::product/widget/viewed/grid.phtml"
before="footer_new_links">
<arguments>
<argument name="uiComponent" xsi:type="string">widget_recently_viewed</argument>
<argument name="page_size" xsi:type="number">16</argument>
<argument name="show_attributes" xsi:type="string">name,image,price,learn_more</argument>
<argument name="show_buttons" xsi:type="string">add_to_wishlist,add_to_cart</argument>
</arguments>
</block>
</referenceContainer>
</body>
</page>
This will add recently viewed widget in all (well, most of the pages) Magento 2 pages footer section.
How is data passing to the widget from backend
Here I am going to explain how this widget will be rendered with product data. The widget configuration is defined and well, they can be altered by widget_recently_viewed.xml
file. We can do this because the recently viewed widget is basically a uiComponent.
Since the widget is actually a uiComponent, it comes with well-defined facilities which are providing by Magento. One such facility is feed data to the uiComponent. This facility is known as dataSource. In the case of recently-viewed-widget, it uses Magento_Catalog/js/product/provider
js component as it’s data provider. This is defined in the file widget_recently_viewed.xml
file available in the Magento_Catalog
module.
So whenever we visit a product details page, you can see that the Magento_Catalog/js/product/provider
component is fed by the product data. The product data we can see here is actually the same that we get when we use Magento API service “. The data structure is shown below:
{
"items": [
{
"add_to_cart_button": {
"post_data": "{\"action\":\"http:\\/\\/example.test\\/checkout\\/cart\\/add\\/uenc\\/%25uenc%25\\/product\\/1195\\/\",\"data\":{\"product\":\"1195\",\"uenc\":\"%uenc%\"}}",
"url": "http://example.test/checkout/cart/add/uenc/%25uenc%25/product/1195/",
"required_options": true
},
"add_to_compare_button": {
"post_data": null,
"url": "{\"action\":\"http:\\/\\/example.test\\/catalog\\/product_compare\\/add\\/\",\"data\":{\"product\":\"1195\",\"uenc\":\"aHR0cDovL2xpdmVycG9vbC50ZXN0L3Jlc3QvVjEvcHJvZHVjdHMtcmVuZGVyLWluZm8_c2VhcmNoQ3JpdGVyaWFbcGFnZVNpemVdPTEmc2VhcmNoQ3JpdGVyaWFbY3VycmVudFBhZ2VdJTIwPTEmc3RvcmVJZD0xJmN1cnJlbmN5Q29kZT1VU0Qmc2VhcmNoQ3JpdGVyaWFbZmlsdGVyX2dyb3Vwc11bMF1bZmlsdGVyc11bMF1bZmllbGRdPWVudGl0eV9pZCZzZWFyY2hDcml0ZXJpYVtmaWx0ZXJfZ3JvdXBzXVswXVtmaWx0ZXJzXVswXVt2YWx1ZV09MTE5NQ,,\"}}",
"required_options": null
},
"price_info": {
"final_price": 65,
"max_price": 65,
"max_regular_price": 65,
"minimal_regular_price": 65,
"special_price": 65,
"minimal_price": 65,
"regular_price": 59.99,
"formatted_prices": {
"final_price": "<span class=\"price\">$65.00</span>",
"max_price": "<span class=\"price\">$65.00</span>",
"minimal_price": "<span class=\"price\">$65.00</span>",
"max_regular_price": "<span class=\"price\">$65.00</span>",
"minimal_regular_price": null,
"special_price": "<span class=\"price\">$65.00</span>",
"regular_price": "<span class=\"price\">$59.99</span>"
},
"extension_attributes": {
"msrp": {
"msrp_price": "<span class=\"price\">$0.00</span>",
"is_applicable": "",
"is_shown_price_on_gesture": "",
"msrp_message": "",
"explanation_message": "Our price is lower than the manufacturer's "minimum advertised price." As a result, we cannot show you the price in catalog or the product page. <br><br> You have no obligation to purchase the product once you know the price. You can simply remove the item from your cart."
},
"tax_adjustments": {
"final_price": 50,
"max_price": 50,
"max_regular_price": 50,
"minimal_regular_price": 50,
"special_price": 50,
"minimal_price": 50,
"regular_price": 59.99,
"formatted_prices": {
"final_price": "<span class=\"price\">$50.00</span>",
"max_price": "<span class=\"price\">$50.00</span>",
"minimal_price": "<span class=\"price\">$50.00</span>",
"max_regular_price": "<span class=\"price\">$50.00</span>",
"minimal_regular_price": null,
"special_price": "<span class=\"price\">$50.00</span>",
"regular_price": "<span class=\"price\">$59.99</span>"
}
},
"weee_attributes": [],
"weee_adjustment": "<span class=\"price\">$50.00</span>"
}
},
"images": [
{
"url": "http://example.test/static/version1588244834/frontend/Magento/default/en_GB/Magento_Catalog/images/product/placeholder/small_image.jpg",
"code": "recently_viewed_products_grid_content_widget",
"height": 440,
"width": 355,
"label": "Mens European Home Shirt 19/20",
"resized_width": 135,
"resized_height": 135
},
{
"url": "http://example.test/static/version1588244834/frontend/Magento/default/en_GB/Magento_Catalog/images/product/placeholder/small_image.jpg",
"code": "recently_viewed_products_list_content_widget",
"height": 270,
"width": 270,
"label": "Mens European Home Shirt 19/20",
"resized_width": 135,
"resized_height": 135
},
{
"url": "http://example.test/static/version1588244834/frontend/Magento/default/en_GB/Magento_Catalog/images/product/placeholder/small_image.jpg",
"code": "recently_viewed_products_images_names_widget",
"height": 90,
"width": 75,
"label": "Mens European Home Shirt 19/20",
"resized_width": 135,
"resized_height": 135
},
{
"url": "http://example.test/static/version1588244834/frontend/Magento/default/en_GB/Magento_Catalog/images/product/placeholder/small_image.jpg",
"code": "recently_compared_products_grid_content_widget",
"height": 300,
"width": 240,
"label": "Mens European Home Shirt 19/20",
"resized_width": 135,
"resized_height": 135
},
{
"url": "http://example.test/static/version1588244834/frontend/Magento/default/en_GB/Magento_Catalog/images/product/placeholder/small_image.jpg",
"code": "recently_compared_products_list_content_widget",
"height": 207,
"width": 270,
"label": "Mens European Home Shirt 19/20",
"resized_width": 135,
"resized_height": 135
},
{
"url": "http://example.test/static/version1588244834/frontend/Magento/default/en_GB/Magento_Catalog/images/product/placeholder/thumbnail.jpg",
"code": "recently_compared_products_images_names_widget",
"height": 90,
"width": 75,
"label": "Mens European Home Shirt 19/20",
"resized_width": 50,
"resized_height": 50
}
],
"url": "http://example.test/home-ss-euro-jersey-19-20",
"id": 1195,
"name": "Mens European Home Shirt 19/20",
"type": "configurable",
"is_salable": "1",
"store_id": 1,
"currency_code": "USD",
"extension_attributes": {
"wishlist_button": {
"post_data": null,
"url": "{\"action\":\"http:\\/\\/example.test\\/wishlist\\/index\\/add\\/\",\"data\":{\"product\":1195,\"uenc\":\"aHR0cDovL2xpdmVycG9vbC50ZXN0L3Jlc3QvVjEvcHJvZHVjdHMtcmVuZGVyLWluZm8_c2VhcmNoQ3JpdGVyaWFbcGFnZVNpemVdPTEmc2VhcmNoQ3JpdGVyaWFbY3VycmVudFBhZ2VdJTIwPTEmc3RvcmVJZD0xJmN1cnJlbmN5Q29kZT1VU0Qmc2VhcmNoQ3JpdGVyaWFbZmlsdGVyX2dyb3Vwc11bMF1bZmlsdGVyc11bMF1bZmllbGRdPWVudGl0eV9pZCZzZWFyY2hDcml0ZXJpYVtmaWx0ZXJfZ3JvdXBzXVswXVtmaWx0ZXJzXVswXVt2YWx1ZV09MTE5NQ,,\"}}",
"required_options": null
},
"review_html": ""
}
}
]
}
In case you are wondering how Magento provide this details, then it is done by the block class Magento\Catalog\Block\Ui\ProductViewCounter::getCurrentProductData()
. This block is available in every product details page and its template adds below code in frontend:
<script type="text/x-magento-init">
{
"*": {
"Magento_Catalog/js/product/view/provider": {
"data": <?= /* @noEscape */ $block->getCurrentProductData() ?>
}
}
}
</script>
How data is showing in the frontend?
Now I hope it is clear how data is passing to the component. It is time to discuss how this is going to be used in the frontend.
I already mentioned that the recently-viewed-widget is actually a uiComponent. It is basically Listing
type uiComponent which means it is basically Magento_Ui/js/grid/listing
component. You can see following mapping in this component.
....
return Collection.extend({
defaults: {
...
imports: {
rows: '${ $.provider }:data.items'
},
....
},
....
As you can see it map ${provider}:data.items
details to rows
property. this.rows
is what actually used by listing component to show the details. The only question is here is what is ${provider}
mentioning above. It is as I mentioned Magento_Catalog/js/product/provider
. You can see below in this component:
...
return Element.extend({
....
dataStorageHandler: function (dataStorage) {
this.productStorage = dataStorage;
this.productStorage.add(this.data.items);
},
...
what we want to understand here is the data this.data.items
is stored into local storage with the key product_data_storage
and this is what recently-viewed-widget uiComponent is holding in this.rows
.
So long story short, if you want to see the the product data details which is using by recently-viewed-widget can be seen in your browser under local-storage section. In chrome browser it will look like this.
I hope next time you see a recently viewed widget or recently compared widget, the above points will be definitely going to help you in great extend. 🙂