Excuse me for the bad wording in the title.. It's a little hard to explain what I'm trying to accomplish in a few words.
I'm trying to add a custom field to the shipping address and billing address forms in the Magento 2.1.2 checkout with a custom uiComponent that applies its own bindings / listeners to that field. Below I'll try to explain what I already did (simplified):
I have added the field myCustomField
to both the shipping and billing forms, using the following XML in checkout_index_index.xml
for the shipping form:
<item name="myCustomField" xsi:type="array">
<item name="component" xsi:type="string">Vendor_MyModule/js/view/myCustomField</item>
<item name="config" xsi:type="array">
<item name="customScope" xsi:type="string">shippingAddress</item>
<item name="template" xsi:type="string">Vendor_MyModule/form/myCustomField</item>
<item name="id" xsi:type="string">myCustomField</item>
</item>
<item name="dataScope" xsi:type="string">shippingAddress.myCustomField</item>
<item name="provider" xsi:type="string">checkoutProvider</item>
<item name="visible" xsi:type="boolean">true</item>
<item name="sortOrder" xsi:type="string">45</item>
</item>
For the billing form I have added the a similar configuration through a plugin/interceptor (Vendor/MyModule/Block/Plugin/Checkout/LayoutProcessor/Plugin.php
):
public function afterProcess(\Magento\Checkout\Block\Checkout\LayoutProcessor $subject, $result){
//Loop over all payment methods
foreach($result['components']['checkout']['children']['steps']['children']['billing-step']['children']
['payment']['children']['payments-list']['children'] as $key => $paymentMethodForm) {
//Get the payment method name (without -form)
$paymentMethod = explode("-", $key)[0];
if(array_key_exists("form-fields", $paymentMethodForm['children'])){
if(!array_key_exists('myCustomField', $paymentMethodForm['children']['form-fields']['children'])){
$result['components']['checkout']['children']['steps']['children']['billing-step']['children']
['payment']['children']['payments-list']['children'][$key]['children']['form-fields']['children']['myCustomField'] = [
"component" => "Vendor_MyModule/js/view/myCustomField",
"config" => [
"customScope" => "billingAddress" . $paymentMethod,
"template" => "Vendor_MyModule/form/myCustomField",
"id" => "myCustomField_" . $key
],
"dataScope" => "billingAddress" . $paymentMethod . ".myCustomField",
"provider" => "checkoutProvider",
"visible" => "true",
"sortOrder" => "45"
];
}
}
}
return $result;
}
This is what the Vendor_MyModule/js/view/myCustomField
component / viewmodel looks like:
define(
[
'ko',
'jquery',
'Magento_Ui/js/form/element/abstract'
],
function(ko, $, Abstract) {
'use strict';
return Abstract.extend({
myValue: ko.observable(),
defaults: {
listens: {
myValue: 'changed',
}
},
initialize: function(){
this._super();
return this;
},
changed: function(value){
console.log("Myvalue was changed!", value);
}
});
}
);
And this is what the template looks like:
<div class="field" data-bind="visible: visible, attr: {'name': element.dataScope}, css: additionalClasses">
<div class="fieldset">
<div class="field">
<label for="myCustomField" data-bind="i18n: 'My custom field'"></label>
<div class="control">
<input class="input-text myCustomField" id="myCustomField" type="text" name="myCustomField" data-bind="textInput: myValue" />
</div>
</div>
</div>
</div>
All this successfully renders an additional field in the shipping address form and (all of) the billing address forms. As you can see I have set up an observable variable myValue: ko.observable()
and I'm using data-bind="textInput: myValue"
on the input to bind myValue
to the input field. The listens: {}
object in my viewmodel makes the changed()
function execute whenever the myValue
observable is changed.
However, when I change the value of the input field in any of the forms, it also gets updated in ALL of the other forms. With the above code in place, I get 47 console.log entries saying 'Myvalue was changed!' any time I enter a character in the input field. I'm guessint this happens because the page contains multiple input fields that all bind to the myValue
observable.
How do I make sure that the observable, listeners etc. are only called for the current input field I'm editing, and not for all occurrences of the input field anywhere on the page? In other words: how do I 'isolate' my viewmodel so it knows it should only apply its functionality to the input field being changed?
Thanks!