Knockout Js is an open source Javascript Library that has an ability to provide an object that links view and models of MVVM design pattern which always keeps them synchronized. KO Js enables the two-way binding using the view-models. It is used for declarative binding which means the developer can state the properties which needs to be bind on the HTML template while all the other actions regarding binding the properties will be taken care of by the Library itself.
In Magento 2, Knockout Js is used to build the dynamic contents in the frontend say checkout page, mini cart and UI components which are prevalent in Magento admin. In this blog we are going to learn on Initializing and binding the options to date picker using Knockout Js in Magento 2 Checkout page.
Let us begin with a user story where the user has to choose the options from the created fields under the shipping method.
- When the shopper chooses shipping method, a datepicker field will be displayed under it.
- Based on the shipping method the starting time and closing time will be different.
- We will take ‘Table Rate’ and ‘Flat Rate’ shipping method for learning the datepicker initialization.
Before we jump on to create the date field, let us see the path of the Knockout components (Js files) and the html template from which the properties are displayed.
The KO components reside in Custom modules under,
app/code/<vendor_name>/<module_name>/view/frontend/web/js/
And in Magento modules under,
vendor/magento/<vendor_name>/<module_name>/view/frontend/web/js/
The KO template reside in Custom modules under,
app/code/<vendor_name>/<module_name>/view/frontend/web/template/
And in Magento modules under,
vendor/magento/<vendor_name>/<module_name>/view/frontend/web/template/
**It is important to make a note that Knockout template and the phtml template files reside at different places.
Now we will begin with initializing the Knockout variables in a component for placing them in the checkout page below the shipping method. I have created a Vendor DCKAP and module name as KnockoutJs. To add the new fields under shipping method follow the below steps.
Step 1: create a checkout_index_index.xml under the path app/code/DCKAP/KnockoutJs/view/frontend/layout to override the shipping method template
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="checkout.root"> <arguments> <argument name="jsLayout" xsi:type="array"> … <item name="shippingAddress" xsi:type="array"> <item name="config" xsi:type="array"> <item name="shippingMethodItemTemplate" xsi:type="string">DCKAP_KnockoutJs/shipping-address/shipping-method-item</item> </item> </item> ... </argument> </arguments> </referenceBlock> </body> </page>
Step 2: Create shipping.js under the path app/code/DCKAP/KnockoutJs/view/frontend/web/js/view/ with a mixin for Magento_Checkout/js/view/shipping component as follows,
The Start and closing time are initialized as KO observables and there are some values which depends on the other factors that KO is already observing. As per our case, the date picker options are dependending on the shipping method and gets initialized. To handle this scenario the KO has provided the computed observables (this.selectedMethod).
define([ 'jquery', 'ko', 'Magento_Checkout/js/model/quote' ], function ( $, ko, quote ) { 'use strict'; var mixin = { defaults: { startTimeArray: [6,7], closeTimeArray: [16,17], localWeekend: [0,6] //Weekend Sunday and Saturday }, initialize: function (config) { this.storeCustomDate = ko.observable(new Date()); this.startTime = ko.observable(); this.closeTime = ko.observable(); this._super(); } }; return function (target) { return target.extend(mixin); }; } );
this.selectedMethod method will be computed whenever the shopper switches between shipping methods. The selected method is called from the KO template before binding the date picker value by passing the chosen shipping method to the computed observable. By matching the parameter received with the quote shipping method we decide to initialize the corresponding datepicker options. For instance, We have provided the start and closing time as 7AM – 5PM for Flat Rate shipping method which can be identified from the code provided.
initObservable: function () { this._super(); this.selectedMethod = function(present_method){ return ko.computed(function () { var method = quote.shippingMethod(); var selectedMethod = method != null ? method.method_code + '_' + method.carrier_code : null; if (selectedMethod === 'bestway_tablerate') { this.startTime = this.startTimeArray[0]; this.closeTime = this.closeTimeArray[0]; } else if (selectedMethod === 'flatrate_flatrate'){ this.startTime = this.startTimeArray[1]; this.closeTime = this.closeTimeArray[1]; } return selectedMethod === present_method; }, this); }; return this; },
The local weekend variables has values as 0 and 6 which are the equivalent code of Sunday and Saturday respectively. This variable will be used in the beforeShowDay option to disable the datepicker from weekends.
getMinDate: function() { var currentDate = new Date(), currentTime, currentDay, i, localeWeekend = this.getLocalWeekend(), startTime = this.startTime; currentTime = currentDate.getHours(); if(currentTime >= this.closeTime){ currentDate = new Date(currentDate.setDate(currentDate.getDate() + 1)); } currentDay = currentDate.getDay(); var dateFactor = function(currentDate) { if(localeWeekend.indexOf(currentDay) >=0 ) { currentDate = new Date(currentDate.setDate(currentDate.getDate() + 1)); currentDay = currentDate.getDay(); return dateFactor(currentDate); } return currentDate; }; currentDate = dateFactor(currentDate); currentDate.setHours(startTime); currentDate.setMinutes(0); return currentDate; }, getLocalWeekend: function() { return this.localWeekend; }
The method getMinDate provides the minimum selectable date from date picker, this feature includes showing the selectable date from current date and reset the hours and minutes chosen based on the start and closing time.
Step 3: Configure the mixin in app/code/DCKAP/KnockoutJs/view/frontend/requirejs-config.js
var config = { config: { mixins: { 'Magento_Checkout/js/view/shipping': { 'DCKAP_KnockoutJs/js/view/shipping': true } } } };
Step 4: Override the shipping method template in the following path app/code/DCKAP/KnockoutJs/view/frontend/web/template/shipping-address/shipping-method-item.html
<!-- ko if: ( element.selectedMethod(method.method_code + '_' + method.carrier_code) ) --> <tr class="row"> <td colspan="4"> <div class="field _required"> <label class="label"> <span>Date: </span> </label> <div class="control"> <input class="input-text" type="text" data-bind="datepicker: { storage: element.storeCustomDate, options: { showsTime: true, minuteMax:0, showMinute:false, minDate: element.getMinDate(), hourMin: element.startTime, hourMax: element.closeTime, timeFormat: 'HH:mm', beforeShowDay: function(datevar){ var date = new Date(datevar), dayName = date.getDay(), count = element.getLocalWeekend().length, i, localWeekend = element.getLocalWeekend(); for (i = 0; i < count; i++) { if (dayName === localWeekend[i]){ return [false]; } } return [true]; } } }" name="date_field" id="date-field" > </div> </div> </td> </tr> <!-- /ko -->
The date picker is defined in the Knockout template as shown below, where hour Min and hour Max are the options we are using for illustration of start and closing time, before ShowDay option disables shopper from choosing the weekends from date picker.
In search of ways to optimize your eCommerce website? Contact us to help you meet the web standards with cutting-edge digital commerce solutions.