Magento 2 – How to Add an Observable to Template Form for Onepage Checkout

checkoutknockoutjsmagento-2.1magento2

I am trying to add a checkbox to the checkout page in Magento2, but I am having issues with it. I have worked through http://devdocs.Magento.com/guides/v2.0/howdoi/checkout/checkout_form.html and successfully add a checkout form with a button, which works all well and good. I am able to bind an element to a function, but I need to bind a checkbox to an observable.

Now, I have read a I need an observable because when I try to click the checkbox, it remains empty. I don't know how to do that and am looking for some assistance.

I have seen in other questions code similar to:

var viewModel = {
    setInstoreShipping: ko.observable(false)
};

viewModel.setInstoreShipping.subscribe(function (newState) {
    if (newState) {
        alert("Checked!");
    } else {
        alert("Unchecked!");
    }
});
ko.applyBindings(viewModel);

but when applying this, I receive an error:
You cannot apply bindings multiple times to the same element. So my question is, how do I bind a checkbox to an observable and how do I create the observable to bind to?

Best Answer

It's because checkout page already do knockout binding when it is rendered. You need to create an UI component and work with that. Your component is defined in checkout_index_index.xml file, like this:

<?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">
                                                        <item name="shippingAdditional" xsi:type="array">
                                                            <item name="component" xsi:type="string">uiComponent</item>
                                                            <item name="displayArea" xsi:type="string">shippingAdditional</item>
                                                            <item name="children" xsi:type="array">
                                                                <item name="my_element" xsi:type="array">
                                                                    <item name="component" xsi:type="string">Vendor_Module/js/view/checkout/custom</item>
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

Then create component js file similar to this

define([
    'ko',
    'uiComponent'
], function (ko, Component) {
    'use strict';

    return Component.extend({
        defaults: {
            template: 'Vendor_Module/checkout/custom',
        },

        initialize: function (config) {
            this.instoreShipping= ko.observable(false);
            return this._super(config);
        },

        setInstoreShipping: function(value) {
            this.instoreShipping(value == true);
        }
    });
});

and then in html file

<form id="custom-checkout-checkbox">
    <input type="checkbox" name="custom-checkbox" id="custom-checkbox" data-bind="change: setInstoreShipping" />
</form>