Magento 2引入了一种新的方式来将产品添加到购物车中。系统现在提供完整的异步(ajax)进程,但在默认应用程序状态下进程本身未激活,它需要在模板内的脚本调用中进行一些手动调整。
介绍
尽管Magento ajax添加到购物车过程提供更好的体验和可用性,(大多数用户在将每个产品添加到购物车中之后,会因为连续地页面重载而感到沮丧)Magento通过简单传输完成工作并显示成功或错误消息,虽然这确实是一个坚实的基础,但我们仍然看到一些使用性问题。让我们从客户的角度来考虑一下,我们正在浏览商店,目前位于产品页面上,随时可以触发购物车按钮将我们想要的产品添加到购物车中。我们向下滚动了一点,可能是为了检查产品规格。按钮是可见的,但页面顶部和标题迷你不是。我们正在触发按钮。当进程开始时,按钮将其默认状态形式“add to cart”更改为“adding”,当流程完成时,将状态更改为“added”,然后返回到默认状态,这是在我们的产品实际添加后我们在页面上唯一的指示。
这种情况给我们留下了很大的改进空间。我们如何改进这个过程?很简单,ajax完成,我们就可以触发滚动事件滚动页面的顶部,,一旦minicart得到更新,我们就可以触发minicart UI对话框打开并告诉我们完整的体验以及minicart实际为我们我们提供的内容(可以查看购物车、结帐或继续购物)。
在这篇博客中,我将演示一种可行的方法。
启用ajax添加到购物车进程
首先,我们需要熟悉背后的架构。
在[magento2_root_dir] /vendor/magento/module_catalog/view/frontend/templates/product/view/add-to-cart.phtml中,有一个addtocart.js组件的脚本调用,我们可以看到它的“bindSubmit”方法被设置为false。第一步是将其更改为true,从而启用ajax添加到购物车流程。
在主题中覆盖此文件
<script type="text/x-magento-init">
{
"#product_addtocart_form": {
"catalogAddToCart": {
"bindSubmit": false // change it to true
}
}
}
</script>
好的,我们已经成功启用了ajax添加到购物车流程。下一步是什么?从模板中的脚本初始化代码片段中,我们可以得到有价值的信息,即脚本实际负责该流程。脚本别名catalogAddToCart实际上指向[magento2_root_dir] /vendor/magento/module_catalog/view/frontend/web/js/catalog-add-to-cart.js javascript组件。我们不会修改脚本本身,我们将进一步扩展ajaxSubmit方法,这样我们就可以在单独的地方进行自定义修改,并尽可能保持流程的其余部分的整洁。该过程需要对jQuery UI widget factory有专门的了解,以便熟悉该方法。
扩展小部件负责建立ajax调用
主要目标是扩展$.mage.catalogAddToCart小部件并修改它的ajaxSubmit方法。如果您不熟悉UI factory widget扩展方法,我建议在继续之前先熟悉UI widget factory。我不会详细介绍核心扩展过程,你可以在这里熟悉jQuery文档站点。
我们将在自定义主题中创建新的require-js脚本组件,并使用require-js define module procedure将catalog-add-to-cart脚本作为依赖项调用。
我们将在自定义主题中创建新的require-js脚本组件,且使用require-js 定义模块过程作为调用catalog-add-to-cart脚本的依赖。
首先,在[magento2_root_dir]/app/design/frontend/[your_vendor_dir]/[your_theme_dir]/web/js/目录中创建脚本。为了本文的目的,我们将调用magease-ajax-cart.js,脚本需要定义为require js模块。在脚本内部,我们将扩展$ .mage.catalogAddToCart并覆盖ajaxSubmit方法,添加滚动动画以在ajax调用完成后滚动到页面顶部。从原始文件中简单复制整个ajaxSubmit方法,下面的代码演示了如何做到这一点。
/**
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
define([
'jquery',
'mage/translate',
'jquery/ui',
'Magento_Catalog/js/catalog-add-to-cart'
], function($, $t) {
"use strict";
$.widget('magease.ajax', $.mage.catalogAddToCart, {
ajaxSubmit: function(form) {
var self = this;
$(self.options.minicartSelector).trigger('contentLoading');
self.disableAddToCartButton(form);
// setting global variable required for customized ajax cart process
window.ajaxCartTransport = false;
$.ajax({
url: form.attr('action'),
data: form.serialize(),
type: 'post',
dataType: 'json',
beforeSend: function() {
if (self.isLoaderEnabled()) {
$('body').trigger(self.options.processStart);
}
},
success: function(res) {
if (self.isLoaderEnabled()) {
$('body').trigger(self.options.processStop);
}
if (res.backUrl) {
window.location = res.backUrl;
return;
}
if (res.messages) {
$(self.options.messagesSelector).html(res.messages);
}
if (res.minicart) {
$(self.options.minicartSelector).replaceWith(res.minicart);
$(self.options.minicartSelector).trigger('contentUpdated');
}
if (res.product && res.product.statusText) {
$(self.options.productStatusSelector)
.removeClass('available')
.addClass('unavailable')
.find('span')
.html(res.product.statusText);
}
self.enableAddToCartButton(form);
// animate scrolling to the top of the page
$("html, body").animate({ scrollTop: 0 }, 1000, function() {});
// changing global variable value to true (this flag enables communication with update minicart logic)
window.ajaxCartTransport = true;
}
});
}
});
return $.magease.ajax;
});
在requirejs-config中映射新创建的自定义js组件
第二步是将脚本映射到主题目录根目录下的requirejs-config.js文件中。
var config = {
map: {
"*": {
"custom-ajaxcart": "js/magease-ajax-cart"
}
}
};
不要忘记检查脚本是否实际加载。
在模板文件中初始化新的自定义js组件
第三步是实际返回到初始化原始脚本的模板,并初始化自定义脚本而不是默认脚本。请记住,我们仍然将默认脚本加载为依赖项。
<script type="text/x-magento-init">
{
"#product_addtocart_form": {
// we need to call our script by alias previously mapped in requirejs-config.js file
"custom-ajaxcart": {
"bindSubmit": true
}
}
}
</script>
现在回到我们的自定义脚本文件,你可以看到我们依赖于success方法,一旦我们从服务器获得状态200 OK。我们使用jQuery将滚动事件设置为滚动到页面顶部。您可以使用您喜欢的任何方法,但是由于Magento中已经包含jQuery,所以我使用了它。
也许你现在正在质疑自己,除了滚动到顶部方法之外,为什么代码中没有其他东西?我们的代码逻辑在哪里负责打开minicart对话框?答案是,没有代码!为什么?原因很简单,除了指示后请求实际完成之外,我们仍然没有得到更多。Magento发出另一个/单独的调用,用更新的状态填充minicart。我们需要知道这个调用,我们需要在使用新数据更新minicart后打开购物车对话框。
我们不想从这里立即展开minicart UI对话框,因为数据仍然没有更新。打开对话框的最佳时间是在更新minicart并填充新数据之后。由于这两个进程在前端没有连接,我们需要设置某种标志,以便知道何时打开购物车对话框。您将看到,我在文件顶部创建了ajaxCartTransport变量,并将该变量分配给window对象,使其成为全局变量并始终可访问。这个标志将进一步连接我们的进程,一旦ajax成功并完成滚动过程,您将看到我们正在强制将变量值更改为true。这是我们在下一步中用来触发购物车对话框打开的标志。
现在我们知道ajax调用在哪里了,下一步是找到使用新数据更新minicart的逻辑。
修改负责更新minicart数据的js组件
该逻辑位于[magento2_root_dir] /vendor/magento/module-checkout/view/frontend/web/js/view/minicart.js文件。由于这个文件是UI组件和独立逻辑的混合,我建议您在主题中复制并覆盖整个脚本。
现在,我们需要找到数据更新的实际位置。它在UI组件更新方法内部,在更新方法中,我们将添加我们的小逻辑来扩展minicart下拉对话框,下面是一个简单的例子。
/**
* 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);
// our logic for opening the minicart
if(window.ajaxCartTransport == true) {
// finding the minicart wrapper element
var minicart = $('[data-block="minicart"]');
// finding the dropdown element itself and invoking dropdownDialog open method
minicart.find('[data-role="dropdownDialog"]').dropdownDialog('open');
// setting our custom global variable immediately back to false
window.ajaxCartTransport = false;
}
}, this);
},
我们在这做了什么?首先,我们检查了我们的标志,以确保只有在ajax调用不久前触发时才展开对话框。我们将检查全局变量ajaxCartTransport是否设置为true,如果是,我们将打开minicart对话框。我们将以最佳方式打开它,调用ui .dropdowndialog('open')方法。JQuery widget factory 提供了一个很好的功能,小部件方法调用。我们需要确保之后立即将我们的标志设置为false,以防止在未连接到我们的ajax添加到购物车自定义进程的每个其他更新进程上打开minicart时出错。