I want to implement the qty increment and decrement option in the mini cart on the homepage.
-
Could you describe what have you done so far?– JimmyMar 30, 2021 at 2:32
-
I am tried this, but not working meetanshi.com/blog/…– zusMar 30, 2021 at 7:03
-
You have everything. If it's not working out of the box, you should check the flow to see where it get wrong.– JimmyMar 30, 2021 at 7:15
-
If you checked the source code, read it, follow it and still don't understand, you can always post your question about the flow here.– JimmyMar 30, 2021 at 7:15
1 Answer
1)Create a file in Vendor\module\View\frontend\web\js\view\minicart.js and add following code:
define([
'uiComponent',
'Magento_Customer/js/customer-data',
'jquery',
'ko',
'underscore',
'sidebar',
'mage/translate',
'mage/dropdown'
], function (Component, customerData, $, ko, _) {
'use strict';
var sidebarInitialized = false,
addToCartCalls = 0,
miniCart;
miniCart = $('[data-block=\'minicart\']');
/**
* @return {Boolean}
*/
function initSidebar() {
if (miniCart.data('mageSidebar')) {
miniCart.sidebar('update');
}
if (!$('[data-role=product-item]').length) {
return false;
}
miniCart.trigger('contentUpdated');
if (sidebarInitialized) {
return false;
}
sidebarInitialized = true;
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',
'qtyDecreasing': '.decreasing-qty',
'qtyIncreasing': '.increasing-qty'
},
'confirmMessage': $.mage.__('Are you sure you would like to remove this item from the shopping cart?')
});
}
miniCart.on('dropdowndialogopen', function () {
initSidebar();
});
return Component.extend({
shoppingCartUrl: window.checkout.shoppingCartUrl,
maxItemsToDisplay: window.checkout.maxItemsToDisplay,
cart: {},
initialize: function () {
var self = this,
cartData = customerData.get('cart');
this.update(cartData());
cartData.subscribe(function (updatedCart) {
addToCartCalls--;
this.isLoading(addToCartCalls > 0);
sidebarInitialized = false;
this.update(updatedCart);
initSidebar();
}, this);
$('[data-block="minicart"]').on('contentLoading', function () {
addToCartCalls++;
self.isLoading(true);
});
if (
cartData().website_id !== window.checkout.websiteId && cartData().website_id !== undefined ||
cartData().storeId !== window.checkout.storeId && cartData().storeId !== undefined
) {
customerData.reload(['cart'], false);
}
return this._super();
},
//jscs:enable requireCamelCaseOrUpperCaseIdentifiers
isLoading: ko.observable(false),
initSidebar: initSidebar,
/**
* Close mini shopping cart.
*/
closeMinicart: function () {
$('[data-block="minicart"]').find('[data-role="dropdownDialog"]').dropdownDialog('close');
},
/**
* @param {String} productType
* @return {*|String}
*/
getItemRenderer: function (productType) {
return this.itemRenderer[productType] || 'defaultRenderer';
},
/**
* Update mini shopping cart content.
*
* @param {Object} updatedCart
* @returns void
*/
update: function (updatedCart) {
_.each(updatedCart, function (value, key) {
if (!this.cart.hasOwnProperty(key)) {
this.cart[key] = ko.observable();
}
this.cart[key](value);
}, this);
},
/**
* Get cart param by name.
*
* @param {String} name
* @returns {*}
*/
getCartParamUnsanitizedHtml: function (name) {
if (!_.isUndefined(name)) {
if (!this.cart.hasOwnProperty(name)) {
this.cart[name] = ko.observable();
}
}
return this.cart[name]();
},
/**
* @deprecated please use getCartParamUnsanitizedHtml.
* @param {String} name
* @returns {*}
*/
getCartParam: function (name) {
return this.getCartParamUnsanitizedHtml(name);
},
/**
* Returns array of cart items, limited by 'maxItemsToDisplay' setting
* @returns []
*/
getCartItems: function () {
var items = this.getCartParamUnsanitizedHtml('items') || [];
items = items.slice(parseInt(-this.maxItemsToDisplay, 10));
return items;
},
/**
* Returns count of cart line items
* @returns {Number}
*/
getCartLineItemsCount: function () {
var items = this.getCartParamUnsanitizedHtml('items') || [];
return parseInt(items.length, 10);
}
});
});
2)Create a file in Vendor\module\View\frontend\web\js\sidebar.js and add following code:
define([
'jquery',
'Magento_Customer/js/model/authentication-popup',
'Magento_Customer/js/customer-data',
'Magento_Ui/js/modal/alert',
'Magento_Ui/js/modal/confirm',
'jquery/ui',
'mage/decorate',
'mage/collapsible',
'mage/cookies'
], function ($, authenticationPopup, customerData, alert, confirm) {
$.widget('mage.sidebar', {
options: {
isRecursive: true,
minicart: {
maxItemsVisible: 3
}
},
scrollHeight: 0,
/**
* Create sidebar.
* @private
*/
_create: function () {
this._initContent();
},
/**
* Update sidebar block.
*/
update: function () {
$(this.options.targetElement).trigger('contentUpdated');
this._calcHeight();
this._isOverflowed();
},
_initContent: function () {
var self = this,
events = {};
this.element.decorate('list', this.options.isRecursive);
events['click ' + this.options.button.close] = function (event) {
event.stopPropagation();
$(self.options.targetElement).dropdownDialog('close');
};
events['click ' + this.options.button.checkout] = $.proxy(function () {
var cart = customerData.get('cart'),
customer = customerData.get('customer');
if (!customer().firstname && cart().isGuestCheckoutAllowed === false) {
// set URL for redirect on successful login/registration. It's postprocessed on backend.
$.cookie('login_redirect', this.options.url.checkout);
if (this.options.url.isRedirectRequired) {
location.href = this.options.url.loginUrl;
} else {
authenticationPopup.showModal();
}
return false;
}
location.href = this.options.url.checkout;
}, this);
events['click ' + this.options.button.remove] = function (event) {
event.stopPropagation();
confirm({
content: self.options.confirmMessage,
actions: {
confirm: function () {
self._removeItem($(event.currentTarget));
},
always: function (event) {
event.stopImmediatePropagation();
}
}
});
};
events['keyup ' + this.options.item.qty] = function (event) {
self._showItemButton($(event.target));
};
events['click ' + this.options.item.button] = function (event) {
event.stopPropagation();
self._updateItemQty($(event.currentTarget));
};
events['focusout ' + this.options.item.qty] = function (event) {
self._validateQty($(event.currentTarget));
};
// The bellow codes will execute when you click on the decrease button
events['click ' + this.options.item.qtyDecreasing] = function (event) {
event.stopPropagation();
var itemId = $(event.currentTarget).data('cart-item');
var qtyElement = $('#cart-item-'+itemId+'-qty');
var qtyValue = parseInt(qtyElement.val());
qtyValue = qtyValue - 1;
if (qtyValue <= 0) {
qtyValue = 1;
}
qtyElement.val(qtyValue).trigger('keyup');
};
// The bellow codes will execute when you click on the increase button
events['click ' + this.options.item.qtyIncreasing] = function (event) {
event.stopPropagation();
var itemId = $(event.currentTarget).data('cart-item');
var qtyElement = $('#cart-item-'+itemId+'-qty');
var qtyValue = parseInt(qtyElement.val());
qtyValue = qtyValue + 1;
if (qtyValue > 100) {
qtyValue = 100;
}
qtyElement.val(qtyValue).trigger('keyup');
};
this._on(this.element, events);
this._calcHeight();
this._isOverflowed();
},
/**
* Add 'overflowed' class to minicart items wrapper element
*
* @private
*/
_isOverflowed: function () {
var list = $(this.options.minicart.list),
cssOverflowClass = 'overflowed';
if (this.scrollHeight > list.innerHeight()) {
list.parent().addClass(cssOverflowClass);
} else {
list.parent().removeClass(cssOverflowClass);
}
},
_showItemButton: function (elem) {
var itemId = elem.data('cart-item'),
itemQty = elem.data('item-qty');
if (this._isValidQty(itemQty, elem.val())) {
$('#update-cart-item-' + itemId).show('fade', 300);
} else if (elem.val() == 0) {
this._hideItemButton(elem);
} else {
this._hideItemButton(elem);
}
},
/**
* @param origin - origin qty. 'data-item-qty' attribute.
* @param changed - new qty.
* @returns {boolean}
* @private
*/
_isValidQty: function (origin, changed) {
return (origin != changed) &&
(changed.length > 0) &&
(changed - 0 == changed) &&
(changed - 0 > 0);
},
/**
* @param {Object} elem
* @private
*/
_validateQty: function (elem) {
var itemQty = elem.data('item-qty');
if (!this._isValidQty(itemQty, elem.val())) {
elem.val(itemQty);
}
},
_hideItemButton: function (elem) {
var itemId = elem.data('cart-item');
$('#update-cart-item-' + itemId).hide('fade', 300);
},
_updateItemQty: function (elem) {
var itemId = elem.data('cart-item');
this._ajax(this.options.url.update, {
item_id: itemId,
item_qty: $('#cart-item-' + itemId + '-qty').val()
}, elem, this._updateItemQtyAfter);
},
/**
* Update content after update qty
*
* @param elem
*/
_updateItemQtyAfter: function (elem) {
this._hideItemButton(elem);
},
_removeItem: function (elem) {
var itemId = elem.data('cart-item');
this._ajax(this.options.url.remove, {
item_id: itemId
}, elem, this._removeItemAfter);
},
/**
* Update content after item remove
*
* @param {Object} elem
* @private
*/
_removeItemAfter: function (elem) {
var productData = customerData.get('cart')().items.find(function (item) {
return Number(elem.data('cart-item')) === Number(item['item_id']);
});
$(document).trigger('ajax:removeFromCart', productData['product_sku']);
},
/**
* @param {String} url - ajax url
* @param {Object} data - post data for ajax call
* @param {Object} elem - element that initiated the event
* @param {Function} callback - callback method to execute after AJAX success
*/
_ajax: function (url, data, elem, callback) {
$.extend(data, {
'form_key': $.mage.cookies.get('form_key')
});
$.ajax({
url: url,
data: data,
type: 'post',
dataType: 'json',
context: this,
beforeSend: function () {
elem.attr('disabled', 'disabled');
},
complete: function () {
elem.attr('disabled', null);
}
})
.done(function (response) {
if (response.success) {
callback.call(this, elem, response);
} else {
var msg = response.error_message;
if (msg) {
alert({
content: msg
});
}
}
})
.fail(function (error) {
console.log(JSON.stringify(error));
});
},
/**
* Calculate height of minicart list
*
* @private
*/
_calcHeight: function () {
var self = this,
height = 0,
counter = this.options.minicart.maxItemsVisible,
target = $(this.options.minicart.list),
outerHeight;
self.scrollHeight = 0;
target.children().each(function () {
if ($(this).find('.options').length > 0) {
$(this).collapsible();
}
outerHeight = $(this).outerHeight();
if (counter-- > 0) {
height += outerHeight;
}
self.scrollHeight += outerHeight;
});
target.parent().height(height);
}
});
return $.mage.sidebar;
});
3)2)Create a file in Vendor\module\View\frontend\web\template\minicart\item\default.html and add following code:
<li class="item product product-item" data-role="product-item">
<div class="product">
<!-- ko if: product_has_url -->
<a data-bind="attr: {href: product_url, title: product_name}" tabindex="-1" class="product-item-photo">
<!-- ko foreach: $parent.getRegion('itemImage') -->
<!-- ko template: {name: getTemplate(), data: item.product_image} --><!-- /ko -->
<!-- /ko -->
</a>
<!-- /ko -->
<!-- ko ifnot: product_has_url -->
<span class="product-item-photo">
<!-- ko foreach: $parent.getRegion('itemImage') -->
<!-- ko template: {name: getTemplate(), data: item.product_image} --><!-- /ko -->
<!-- /ko -->
</span>
<!-- /ko -->
<div class="product-item-details">
<strong class="product-item-name">
<!-- ko if: product_has_url -->
<a data-bind="attr: {href: product_url}, html: product_name"></a>
<!-- /ko -->
<!-- ko ifnot: product_has_url -->
<!-- ko text: product_name --><!-- /ko -->
<!-- /ko -->
</strong>
<!-- ko if: options.length -->
<div class="product options" data-mage-init='{"collapsible":{"openedState": "active", "saveState": false}}'>
<span data-role="title" class="toggle"><!-- ko i18n: 'See Details' --><!-- /ko --></span>
<div data-role="content" class="content">
<strong class="subtitle"><!-- ko i18n: 'Options Details' --><!-- /ko --></strong>
<dl class="product options list">
<!-- ko foreach: { data: options, as: 'option' } -->
<dt class="label"><!-- ko text: option.label --><!-- /ko --></dt>
<dd class="values">
<!-- ko if: Array.isArray(option.value) -->
<span data-bind="html: option.value.join('<br>')"></span>
<!-- /ko -->
<!-- ko if: (!Array.isArray(option.value) && ['file', 'html'].includes(option.option_type)) -->
<span data-bind="html: option.value"></span>
<!-- /ko -->
<!-- ko if: (!Array.isArray(option.value) && !['file', 'html'].includes(option.option_type)) -->
<span data-bind="text: option.value"></span>
<!-- /ko -->
</dd>
<!-- /ko -->
</dl>
</div>
</div>
<!-- /ko -->
<div class="product-item-pricing">
<!-- ko if: canApplyMsrp -->
<div class="details-map">
<span class="label" data-bind="i18n: 'Price'"></span>
<span class="value" data-bind="i18n: 'See price before order confirmation.'"></span>
</div>
<!-- /ko -->
<!-- ko ifnot: canApplyMsrp -->
<!-- ko foreach: $parent.getRegion('priceSidebar') -->
<!-- ko template: {name: getTemplate(), data: item.product_price, as: 'price'} --><!-- /ko -->
<!-- /ko -->
<!-- /ko -->
<div class="details-qty qty">
<label class="label" data-bind="i18n: 'Qty', attr: {
for: 'cart-item-'+item_id+'-qty'}"></label>
<button type="button" data-bind="attr: {title: $t('Decrease the quantity'), 'data-cart-item': item_id, 'data-item-qty': qty}" style="background-color:#1979c3;color:#fffcff;" class="decreasing-qty">-</button>
<input data-bind="attr: {
id: 'cart-item-'+item_id+'-qty',
'data-cart-item': item_id,
'data-item-qty': qty,
'data-cart-item-id': product_sku
}, value: qty"
type="number"
size="4"
class="item-qty cart-item-qty"
maxlength="12"/>
<button type="button" data-bind="attr: {title: $t('Increase the quantity'), 'data-cart-item': item_id, 'data-item-qty': qty}" style="background-color:#1979c3;color:#fffcff;" class="increasing-qty">+</button>
<button data-bind="attr: {
id: 'update-cart-item-'+item_id,
'data-cart-item': item_id,
title: $t('Update')
}"
class="update-cart-item"
style="display: none">
<span data-bind="i18n: 'Update'"></span>
</button>
</div>
</div>
<div class="product actions">
<!-- ko if: is_visible_in_site_visibility -->
<div class="primary">
<a data-bind="attr: {href: configure_url, title: $t('Edit item')}" class="action edit">
<span data-bind="i18n: 'Edit'"></span>
</a>
</div>
<!-- /ko -->
<div class="secondary">
<a href="#" data-bind="attr: {'data-cart-item': item_id, title: $t('Remove item')}"
class="action delete">
<span data-bind="i18n: 'Remove'"></span>
</a>
</div>
</div>
</div>
</div>
<div class="message notice" if="$data.message">
<div data-bind="text: $data.message"></div>
</div>
</li>
-
After added it is not visible on mini cart, file structure : snipboard.io/JRz9Qa.jpg– zusMar 31, 2021 at 5:51
-
-
-
-