I'm trying to create a mask for telephone field in Magento 2.2.2 checkout. In order to do so. I placed a checkout_index_index.xml with a new template for telephone field at
vendor/mytheme/Magento_Checkout/layout/checkout_index_index.xml
 <?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" 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="components" xsi:type="array">
 <item name="checkout" xsi:type="array">
 <item name="children" xsi:type="array">
 <item name="steps" xsi:type="array">
 <item name="children" xsi:type="array">
 <item name="shipping-step" xsi:type="array">
 <item name="children" xsi:type="array">
 <item name="shippingAddress" xsi:type="array">
 <item name="children" xsi:type="array">
 <!-- The name of the form the field belongs to -->
 <item name="shipping-address-fieldset" xsi:type="array">
 <item name="children" xsi:type="array">
 <!-- the field you are customizing -->
 <item name="telephone" xsi:type="array">
 <item name="component" xsi:type="string">Magento_Checkout/js/view/masked</item>
 <item name="config" xsi:type="array">
 <!-- Assigning a new template -->
 <item name="elementTmpl" xsi:type="string">Magento_Checkout/form/element/telefone</item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </argument>
 </arguments>
 </referenceBlock>
 </body>
</page>
In the folder vendor/mytheme/web/js/view/masked.js I place bellow code
define(['jquery', 'uiComponent', 'ko', ], function ( ,ドル Component, ko) {
 'use strict';
 return Component.extend({
 defaults: {
 template: 'Magento_Checkout/telefone'
 },
 initialize: function () {
 var self = this;
 this._super();
 ko.bindingHandlers.maskedInput = {
 init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
 ko.bindingHandlers.value.init(element, valueAccessor, allBindings, viewModel, bindingContext);
 },
 update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
 ko.bindingHandlers.value.update(element, valueAccessor, allBindings, viewModel, bindingContext);
 $(element).mask(allBindings.get('mask'));
 valueAccessor()($(element).val());
 }
 };
 var ViewModel = function() {
 this.phone = ko.observable("");
 this.phone('123123451212');
 }; 
 ko.applyBindings(new ViewModel());
 }
 } 
)
}
);
And the I created template telefone.html at vendor/mytheme/Magento_Checkout/web/template
<input class="input-text" type="text" data-bind="
 value: value,
 valueUpdate: 'keyup',
 maskedInput: phone, 
 mask: '(999) 999-9999',
 hasFocus: focused,
 attr: {
 name: inputName,
 placeholder: placeholder,
 'aria-describedby': noticeId,
 id: uid,
 disabled: disabled
 }" />
But when I go to checkout I get error: Uncaught Error: You cannot apply bindings multiple times to the same element. Does someone know a proper way to apply phone mask to telephone input at checkout. Or, if my method is correct, what am I missing or doing wrong?
3 Answers 3
There is seems to be a conflict because below layout you have already mentioned the template of element and re-defined the template in your component js file also,
vendor/mytheme/Magento_Checkout/layout/checkout_index_index.xml
<!-- the field you are customizing -->
<item name="telephone" xsi:type="array">
 <item name="component" xsi:type="string">Magento_Checkout/js/view/masked</item>
 <item name="config" xsi:type="array">
 <!-- Assigning a new template -->
 <item name="elementTmpl" xsi:type="string">Magento_Checkout/form/element/telefone</item>
 </item>
</item>
Hence try replace below component script with your, vendor/mytheme/web/js/view/masked.js
define([
 'underscore', 'ko',
 'mageUtils', 
 'Magento_Ui/js/form/element/abstract',
 'uiLayout'
], function (_, ko, utils, Abstract, layout) {
 'use strict';
 return Abstract.extend({
 initialize: function () {
 this._super();
 //add your custom code or call custom functions
 ko.bindingHandlers.maskedInput = {
 init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
 ko.bindingHandlers.value.init(element, valueAccessor, allBindings, viewModel, bindingContext);
 },
 update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
 ko.bindingHandlers.value.update(element, valueAccessor, allBindings, viewModel, bindingContext);
 $(element).mask(allBindings.get('mask'));
 valueAccessor()($(element).val());
 }
 };
 var ViewModel = function() {
 this.phone = ko.observable("");
 this.phone('123123451212');
 }; 
 ko.applyBindings(new ViewModel());
 return this;
 } 
 });
});
Note : script extends from Magento_Ui/js/form/element/abstract
My goal is just to put a mask for the telephone field in Magento2 Checkout. Since the Knockout method didn't work I simple declared a new template in checkout_index_index.xml with the bellow code:
 <input class="input-text" type="text" placeholder= (__)_____-____ data-bind="
 value: value,
 valueUpdate: 'keyup',
 attr: {
 name: inputName, 
 id: uid
 }" name="telephone" aria-required="true" aria-invalid="false" autocomplete="off" onfocus="this.value='(';" onkeyup="var telephone = this.value; 
 if (telephone.match(/^\(\d{2}$/) !==null) {
 this.value = telephone + ')';
 } else if (telephone.match(/^\(\d{2}\)\d{5}$/) !== null) {
 this.value = telephone + '-';
 }" maxlength="14"/>
Ok, I finally found a better way to do it. I used
https://igorescobar.github.io/jQuery-Mask-Plugin/
Then I create a module in which I create a requirejs-config.js in /vendor/module/view/frontend/requirejs-config.js with following code
var config = {
 map: {
 '*': {
 mask: 'BeeVirtual_Telephone/js/mask' //this the js found in igorescobar github repository
 }
 }
}
So place mask.js at /vendor/module/view/frontend/web/js
Then in checkout_index_index.xml declare your new telephone.html file
 <?xml version="1.0"?>
 <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" 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="components" xsi:type="array">
 <item name="checkout" xsi:type="array">
 <item name="children" xsi:type="array">
 <item name="steps" xsi:type="array">
 <item name="children" xsi:type="array">
 <item name="shipping-step" xsi:type="array">
 <item name="children" xsi:type="array">
 <item name="shippingAddress" xsi:type="array">
 <item name="children" xsi:type="array">
 <!-- The name of the form the field belongs to -->
 <item name="shipping-address-fieldset" xsi:type="array">
 <item name="children" xsi:type="array">
 <!-- the field you are customizing -->
 <item name="telephone" xsi:type="array">
 <item name="component" xsi:type="string">Vendor_Module/js/view/masked</item>
 <item name="config" xsi:type="array">
 <!-- Assigning a new template -->
 <item name="elementTmpl" xsi:type="string">Magento_Checkout/form/element/telefone</item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </item>
 </argument>
 </arguments>
 </referenceBlock>
 </body>
 </page>
Then in my masked.js file
define(
 [
 'underscore',
 'ko',
 'mage/url',
 'jquery',
 'mask',
 ],
 function (_, ko, Component, urlBuilder, jQuery, mask) {
 'use strict';
 ko.bindingHandlers.mask = {
 init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
 var mask = valueAccessor();
 jQuery(element).mask(mask);
 }
 };
});
Then in telephone.html
<input class="input-text" type="text" placeholder= (__)_____-____ data-bind="
 value: value,
 valueUpdate: 'keyup',
 attr: {
 'aria-describedby': getDescriptionId(),
 'aria-invalid': error() ? true : 'false', 
 name: inputName, 
 id: uid,
 mask:'(00)00000-0000'
 }" name="telephone" aria-required="true" aria-invalid="" autocomplete="off" maxlength="14"/> 
Explore related questions
See similar questions with these tags.
applyBindingson it.It will do it automatically