First:
- In step 5 there is a mistake. Depending on "load" event inside require isn't correct.
- It also uses default GET method in ajax request and because of it
later there is a manual call of method to reload customerData 'cart'
section. If we would use POST method we wouldn't have to reload 'cart' section this way.
But "How it load subtotal order and order total?"
Magento 2 in many places uses KnockoutJS to render and update UI elements. In short we can create knockout observables in javascript and if we updated its value it will automatically update on our page.
I recommend to look for some articles about KnockoutJS and how it works in Magento2. It's also important to understand how RequireJs works as it will help you understand Magento2 JS system.
Here I will show you how it works on example provided by you. I hope you will understand little more. But perhaps you will find how much more you need to learn. But remember. Step by step you can understand it. It can just take some time.
When using chrome knockoutjs extensions you can see that subtotal element is rendered via javscript file: Magento_Tax/js/view/checkout/summary/subtotal (js file).
It uses template: "Magento_Tax/checkout/summary/subtotal" (html file located in vendor/magento/module-tax/view/frontend/web/template/checkout/summary/subtotal.html
)
We see there:
<span class="price" data-bind="text: getValue(), attr: {'data-th': excludingTaxMessage}"></span>
So data-bind let us declare bindings. Here we have text
and attr
bindings.
IN short text binding will put value returned by getValue()
inside this span as a "text".
Let's check now what returns this method from subtotal.js file mentioned before.
getValue: function () {
var price = 0;
if (this.totals()) {
price = this.totals().subtotal;
}
return this.getFormattedPrice(price);
}
It returns a price. Most important here is this.totals()
if we look above we see:
define([
'Magento_Checkout/js/view/summary/abstract-total',
'Magento_Checkout/js/model/quote'
], function (Component, quote) {
'use strict';
var displaySubtotalMode = window.checkoutConfig.reviewTotalsDisplayMode;
return Component.extend({
defaults: {
displaySubtotalMode: displaySubtotalMode,
template: 'Magento_Tax/checkout/summary/subtotal'
},
totals: quote.getTotals(),
We see quote.getTotals()
. Where it comes from? It comes from dependency defined at the top. It means that "quote" variable will be represented by object returned by Magento_Checkout/js/model/quote
In quote.js we can find things like:
totals = ko.observable(totalsData),
...
return {
totals: totals,
/**
*
* @return {*}
*/
getTotals: function () {
return totals;
},
/**
* @param {Object} data
*/
setTotals: function (data) {
data = proceedTotalsData(data);
totals(data);
this.setCollectedTotals('subtotal_with_discount', parseFloat(data['subtotal_with_discount']));
},
Here we see that first total are declared as knockout observable (so if we change its value it can be automatically update in UI - if it is used there or we could do few other things).
Then this observable is set as reference to totals
property of object that is returned by this. We also return here some methods like getTotals, setTotals etc.
Now what we need to do to update subtotals/totals in our UI? We just need to call setTotal method using our quote module (quote.js)/
And this is what getTotalsActions method called in code below does.
In cartQtyUpdate.js you see it is called in success callback
success: function (res) {
...
/* Totals summary reloading */
var deferred = $.Deferred();
getTotalsAction([], deferred);
}
To see what is getTotalsAction we need go to the top of this file.
define([
'jquery',
'Magento_Checkout/js/action/get-totals',
'Magento_Customer/js/customer-data'
], function ($, getTotalsAction, customerData) {
getTotalsAction is what Magento_Checkout/js/action/get-totals returns.
Let's see the main part of this file:
return function (callbacks, deferred) {
deferred = deferred || $.Deferred();
totals.isLoading(true);
return storage.get(
resourceUrlManager.getUrlForCartTotals(quote),
false
).done(function (response) {
var proceed = true;
totals.isLoading(false);
if (callbacks.length > 0) {
$.each(callbacks, function (index, callback) {
proceed = proceed && callback();
});
}
if (proceed) {
quote.setTotals(response);
deferred.resolve();
}
}).error(function (response) {
totals.isLoading(false);
deferred.reject();
errorProcessor.process(response);
}).always(function () {
totals.isLoading(false);
});
};
Here we have another AJAX request (we use here get() method from lib/mage/web/storage.js). We set "loader" for totals, get updated value and set totals using setTotals
method from quote.js
Then our knockout observable totals is updated and in the end our UI. This example is little complicated because we see how many files are used here and connected to each other.
So for this part I recommend reading more about knockoutjs, requirejs as well how javascript systems works in magento2 (Magento 2 dev docs, Alan Storm and other resources).
How cart form (main table with items and qty) is updated?
From provided code
success: function (res) {
var parsedResponse = $.parseHTML(res);
var result = $(parsedResponse).find("#form-validate");
var sections = ['cart'];
$("#form-validate").replaceWith(result);
we see it parse the result (html page) and query only form-validate part and replace the current one cart form with update version using jquery method replaceWith.
Few words about how "minicart" is updated?
I won't answer this question completely as I already wrote quite a few things. But after adding product success callback is executed we see customerData.reload(sections, true)
where sections = 'cart'
.
This updates minicart. Here we could use "POST" method instead of "GET" and we wouldn't have to call customerData.reqload...
as Magento2 can automatically update specific sections like "cart" if there is any "POST|PUT|DELETE" requests for specified urls. But I think we would need add those url related to update cart to section.xml to make it work.
How exactly this works? Please read more about this in links provided by me below. As I believe it's already quite nice explained in those resources.