-
-
Notifications
You must be signed in to change notification settings - Fork 2
Minicart
The minicart is built from two components: minicart.js
and sidebar.js
.
minicart.js
provides the skeleton and exposes logic for opening, closing and updating the contennnts of the minicart.
sidebar.js
exposes the logic for displaying the contents of the minicart. It is based on the Ui Component /lib/web/mage/dropdown.js
.
The UI Component is initialised in the template /vendor/magento/module-checkout/view/frontend/templates/cart/minicart.phtml
.
<?php if ($block->getIsNeedToDisplaySideBar()): ?>
<div class="block block-minicart"
data-role="dropdownDialog"
data-mage-init='{"dropdownDialog":{
"appendTo":"[data-block=minicart]",
"triggerTarget":".showcart",
"timeout": "2000",
"closeOnMouseLeave": false,
"closeOnEscape": true,
"triggerClass":"active",
"parentClass":"active",
"buttons":[]}}'>
<div id="minicart-content-wrapper" data-bind="scope: 'minicart_content'">
<!-- ko template: getTemplate() --><!-- /ko -->
</div>
<?= $block->getChildHtml('minicart.addons') ?>
</div>
The data-mage-init
attribute is populated with a JS object which configures the dialog component which appears when the mini cart is clicked on.
At the bottom of the PHTML template, there is a script
block which intialises the mini cart UI Component:
<script type="text/x-magento-init">
{
"[data-block='minicart']": {
"Magento_Ui/js/core/app": <?= /* @escapeNotVerified */ $block->getJsLayout() ?>
},
"*": {
"Magento_Ui/js/block-loader": "<?= /* @escapeNotVerified */ $block->getViewFileUrl('images/loader-1.gif') ?>"
}
}
</script>
Which produces something like this:
<script type="text/x-magento-init">
{
"[data-block='minicart']": {
"Magento_Ui/js/core/app": {"components":{"minicart_content":{"children":{"subtotal.container":{"children":{"subtotal":{"children":{"subtotal.totals":{"config":{"display_cart_subtotal_incl_tax":1,"display_cart_subtotal_excl_tax":0,"template":"Magento_Tax\/checkout\/minicart\/subtotal\/totals"},"children":{"subtotal.totals.msrp":{"component":"Magento_Msrp\/js\/view\/checkout\/minicart\/subtotal\/totals","config":{"displayArea":"minicart-subtotal-hidden","template":"Magento_Msrp\/checkout\/minicart\/subtotal\/totals"}}},"component":"Magento_Tax\/js\/view\/checkout\/minicart\/subtotal\/totals"}},"component":"uiComponent","config":{"template":"Magento_Checkout\/minicart\/subtotal"}}},"component":"uiComponent","config":{"displayArea":"subtotalContainer"}},"item.renderer":{"component":"uiComponent","config":{"displayArea":"defaultRenderer","template":"Trespass_MiniBasket\/minicart\/item\/default"},"children":{"item.image":{"component":"Magento_Catalog\/js\/view\/image","config":{"template":"Magento_Catalog\/product\/image","displayArea":"itemImage"}},"checkout.cart.item.price.sidebar":{"component":"uiComponent","config":{"template":"Magento_Checkout\/minicart\/item\/price","displayArea":"priceSidebar"}}}},"extra_info":{"component":"uiComponent","config":{"displayArea":"extraInfo"}},"promotion":{"component":"uiComponent","config":{"displayArea":"promotion"}}},"config":{"itemRenderer":{"default":"defaultRenderer","simple":"defaultRenderer","virtual":"defaultRenderer"},"template":"Trespass_MiniBasket\/minicart\/content"},"component":"Magento_Checkout\/js\/view\/minicart"}},"types":[]} },
"*": {
"Magento_Ui/js/block-loader": "http://trespass-m2.local/uk/static/frontend/Trespass/default/en_GB/images/loader-1.gif"
}
}
</script>
This is a tree of all the components which will be initialised when this one is.
The [data-block='minicart']
refers to the data-block
attribute of the enclosing div
:
<div data-block="minicart" class="minicart-wrapper">
<a class="action showcart" href="<?= /* @escapeNotVerified */ $block->getShoppingCartUrl() ?>"
data-bind="scope: 'minicart_content'">
<span class="text"><?= /* @escapeNotVerified */ __('My Cart') ?></span>
<span class="counter qty empty"
data-bind="css: { empty: !!getCartParam('summary_count') == false }, blockLoader: isLoading">
<span class="counter-number"><!-- ko text: getCartParam('summary_count') --><!-- /ko --></span>
<span class="counter-label">
<!-- ko if: getCartParam('summary_count') -->
<!-- ko text: getCartParam('summary_count') --><!-- /ko -->
<!-- ko i18n: 'items' --><!-- /ko -->
<!-- /ko -->
</span>
</span>
</a>
<?php if ($block->getIsNeedToDisplaySideBar()): ?>
<div class="block block-minicart" ...
Another script
block exposes the serialised config for the UI Component:
<script>
window.checkout = <?= /* @escapeNotVerified */ $block->getSerializedConfig() ?>;
</script>
Which produces:
<script>
window.checkout = {"shoppingCartUrl":"http:\/\/trespass-m2.local\/uk\/checkout\/cart\/","checkoutUrl":"http:\/\/trespass-m2.local\/uk\/checkout\/","updateItemQtyUrl":"http:\/\/trespass-m2.local\/uk\/checkout\/sidebar\/updateItemQty\/","removeItemUrl":"http:\/\/trespass-m2.local\/uk\/checkout\/sidebar\/removeItem\/","imageTemplate":"Magento_Catalog\/product\/image_with_borders","baseUrl":"http:\/\/trespass-m2.local\/uk\/","minicartMaxItemsVisible":3,"websiteId":"2","maxItemsToDisplay":3,"storeId":"2","customerLoginUrl":"http:\/\/trespass-m2.local\/uk\/customer\/account\/login\/referer\/aHR0cDovL3RyZXNwYXNzLW0yLmxvY2FsL3VrLw%2C%2C\/","isRedirectRequired":false,"autocomplete":"off","captcha":{"user_login":{"isCaseSensitive":false,"imageHeight":50,"imageSrc":"","refreshUrl":"http:\/\/trespass-m2.local\/uk\/captcha\/refresh\/","isRequired":false,"timestamp":1570889172}}};
</script>
The config is defined in the class html/vendor/magento/module-checkout/Block/Cart/Sidebar.php
:
/**
* Returns minicart config
*
* @return array
*/
public function getConfig()
{
return [
'shoppingCartUrl' => $this->getShoppingCartUrl(),
'checkoutUrl' => $this->getCheckoutUrl(),
'updateItemQtyUrl' => $this->getUpdateItemQtyUrl(),
'removeItemUrl' => $this->getRemoveItemUrl(),
'imageTemplate' => $this->getImageHtmlTemplate(),
'baseUrl' => $this->getBaseUrl(),
'minicartMaxItemsVisible' => $this->getMiniCartMaxItemsCount(),
'websiteId' => $this->_storeManager->getStore()->getWebsiteId(),
'maxItemsToDisplay' => $this->getMaxItemsToDisplay(),
'storeId' => $this->_storeManager->getStore()->getId()
];
}
The PHTML template outputs a lot of JS configuration. This is then passed to the UI Component files when they are initialised.
The JS file is html/vendor/magento/module-checkout/view/frontend/web/js/view/minicart.js
.
The minicart UI Component uses the Magento core component 'sidebar', located at: html/vendor/magento/module-checkout/view/frontend/web/js/sidebar.js
.
The sidebar
is itself based on a more generic core component called 'dropdown': html/lib/web/mage/dropdown.js
, which is itself a wrapper for the jQuery UI Dialog widget (see https://api.jqueryui.com/dialog/).
The options for the jQuery UI Dialog widget are merged with the options declared in the dropdown
, sidebar
components. The options defined in the data-mage-init
attribute of the block-minicart
div are also merged with the options
object.
When the dropdowndialogopen
event is triggered (basically when the mini cart is clicked) the initSidebar
method is called:
// Some properties used by the initSidebar method
var sidebarInitialized = false,
addToCartCalls = 0,
miniCart;
// The miniCart DOM element
miniCart = $('[data-block=\'minicart\']');
/**
* @return {Boolean}
*/
function initSidebar() {
if (miniCart.data('mageSidebar')) {
miniCart.sidebar('update');
}
if (!$('[data-role=product-item]').length) {
// If there are no items in the mini cart, exit early
return false;
}
miniCart.trigger('contentUpdated');
// Exit early if sidebar was already initialised, to prevent initialisation running twice
if (sidebarInitialized) {
return false;
}
sidebarInitialized = true;
// Define options which will be passed to the dialog jQuery UI widget
// The window.checkout.* values were all output in the PHTML template
miniCart.sidebar({
'targetElement': 'div.block.block-minicart',
'url': {
'checkout': window.checkout.checkoutUrl,
'update': window.checkout.updateItemQtyUrl,
'remove': window.checkout.removeItemUrl,
'loginUrl': window.checkout.customerLoginUrl,
'isRedirectRequired': window.checkout.isRedirectRequired
},
'button': {
'checkout': '#top-cart-btn-checkout',
'remove': '#mini-cart a.action.delete',
'close': '#btn-minicart-close'
},
'showcart': {
'parent': 'span.counter',
'qty': 'span.counter-number',
'label': 'span.counter-label'
},
'minicart': {
'list': '#mini-cart',
'content': '#minicart-content-wrapper',
'qty': 'div.items-total',
'subtotal': 'div.subtotal span.price',
'maxItemsVisible': window.checkout.minicartMaxItemsVisible
},
'item': {
'qty': ':input.cart-item-qty',
'button': ':button.update-cart-item'
},
'confirmMessage': $.mage.__('Are you sure you would like to remove this item from the shopping cart?')
});
}
// Execute all of the above when the 'dropdowndialogopen' event is triggered
miniCart.on('dropdowndialogopen', function () {
initSidebar();
});