Magento 2: Add a New Step to Checkout Process

Add a new step to checkout process and furthermore add some custom form fields to this new step

Posted on January 22, 2019 in Magento2

Create a new custom Module

You can find a lot of tutorials how to create a Magento 2 Module in my previous tutorials or online. I will skip this step for shortness of length of this tutorial. My new module called, ‘Jeff_CustomCheckout’. In this tutorial, I will add a new step for the checkout called, ‘Pharmacy Questions’. In this step, I will create some form fields, input, checkbox, select, and date. The final result should look like:
custom linked product

Page handle file: checkout_index_index.xml

In any custom module, we first create a checkout_index_index.xml handle file in the magento_root/app/code/Jeff/CustomCheckout/view/frontend/layout.


<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="my-new-step" xsi:type="array">
                                            <item name="component" xsi:type="string">Jeff_CustomCheckout/js/view/my-step-view</item>
                                            <!-- to display step content before shipping step "sortOrder" value should be 1 -->
                                            <!-- to display step content between shipping and payment step 1<..<1 -->
                                            <item name="sortOrder" xsi:type="string">3</item>
                                            <item name="children" xsi:type="array">
                                                <!-- add here child component declaration for your step -->
                                                <item name="field-group-custom" xsi:type="array">
                                                    <item name="component" xsi:type="string">uiComponent</item>
                                                    <item name="displayArea" xsi:type="string">field-group-custom</item>
                                                    <item name="children" xsi:type="array"></item>
                                                </item>
                                            </item>
                                        </item>

                                        <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="before-form" xsi:type="array">
                                                            <item name="children" xsi:type="array">

                                                                    <!-- Your form declaration here -->
                                                                    <item name="custom-checkout-form-container" xsi:type="array">
                                                                        <item name="component" xsi:type="string">Jeff_CustomCheckout/js/view/custom-checkout-form</item>
                                                                        <item name="provider" xsi:type="string">checkoutProvider</item>
                                                                        <item name="config" xsi:type="array">
                                                                            <item name="template" xsi:type="string">Jeff_CustomCheckout/custom-checkout-form</item>
                                                                        </item>
                                                                        <item name="children" xsi:type="array">
                                                                            <item name="custom-checkout-form-fieldset" xsi:type="array">
                                                                                <!-- uiComponent is used as a wrapper for form fields (its template will render all children as a list) -->
                                                                                <item name="component" xsi:type="string">uiComponent</item>
                                                                                <!-- the following display area is used in template -->
                                                                                <item name="displayArea" xsi:type="string">custom-checkout-form-fields</item>
                                                                                <item name="children" xsi:type="array">

                                                                                    <item name="text_field" xsi:type="array">
                                                                                        <item name="component" xsi:type="string">Magento_Ui/js/form/element/abstract</item>
                                                                                        <item name="config" xsi:type="array">
                                                                                            <!-- customScope is used to group elements within a single form -->
                                                                                            <item name="customScope" xsi:type="string">customCheckoutForm</item>
                                                                                            <item name="template" xsi:type="string">ui/form/field</item>
                                                                                            <item name="elementTmpl" xsi:type="string">ui/form/element/input</item>
                                                                                        </item>
                                                                                        <item name="provider" xsi:type="string">checkoutProvider</item>
                                                                                        <item name="dataScope" xsi:type="string">customCheckoutForm.text_field</item>
                                                                                        <item name="label" xsi:type="string">Text Field</item>
                                                                                        <item name="sortOrder" xsi:type="string">1</item>
                                                                                        <item name="validation" xsi:type="array">
                                                                                            <item name="required-entry" xsi:type="string">true</item>
                                                                                        </item>
                                                                                    </item>

                                                                                    <item name="checkbox_field" xsi:type="array">
                                                                                        <item name="component" xsi:type="string">Magento_Ui/js/form/element/boolean</item>
                                                                                        <item name="config" xsi:type="array">
                                                                                            <item name="customScope" xsi:type="string">customCheckoutForm</item>
                                                                                            <item name="template" xsi:type="string">ui/form/field</item>
                                                                                            <item name="elementTmpl" xsi:type="string">ui/form/element/checkbox</item>
                                                                                        </item>
                                                                                        <item name="provider" xsi:type="string">checkoutProvider</item>
                                                                                        <item name="dataScope" xsi:type="string">customCheckoutForm.checkbox_field</item>
                                                                                        <item name="label" xsi:type="string">Checkbox Field</item>
                                                                                        <item name="sortOrder" xsi:type="string">3</item>
                                                                                    </item>

                                                                                    <item name="select_field" xsi:type="array">
                                                                                        <item name="component" xsi:type="string">Magento_Ui/js/form/element/select</item>
                                                                                        <item name="config" xsi:type="array">
                                                                                            <item name="customScope" xsi:type="string">customCheckoutForm</item>
                                                                                            <item name="template" xsi:type="string">ui/form/field</item>
                                                                                            <item name="elementTmpl" xsi:type="string">ui/form/element/select</item>
                                                                                        </item>
                                                                                        <item name="options" xsi:type="array">
                                                                                            <item name="0" xsi:type="array">
                                                                                                <item name="label" xsi:type="string">Please select value</item>
                                                                                                <item name="value" xsi:type="string"></item>
                                                                                            </item>
                                                                                            <item name="1" xsi:type="array">
                                                                                                <item name="label" xsi:type="string">Value 1</item>
                                                                                                <item name="value" xsi:type="string">value_1</item>
                                                                                            </item>
                                                                                            <item name="2" xsi:type="array">
                                                                                                <item name="label" xsi:type="string">Value 2</item>
                                                                                                <item name="value" xsi:type="string">value_2</item>
                                                                                            </item>
                                                                                        </item>
                                                                                        <!-- value element allows to specify default value of the form field -->
                                                                                        <item name="value" xsi:type="string">value_2</item>
                                                                                        <item name="provider" xsi:type="string">checkoutProvider</item>
                                                                                        <item name="dataScope" xsi:type="string">customCheckoutForm.select_field</item>
                                                                                        <item name="label" xsi:type="string">Select Field</item>
                                                                                        <item name="sortOrder" xsi:type="string">2</item> 
                                                                                    </item>

                                                                                </item>
                                                                            </item>
                                                                        </item>
                                                                    </item>

                                                            </item>
                                                        </item>

                                                        <item name="shipping-address-fieldset" xsi:type="array">
                                                            <item name="children" xsi:type="array">
                                                                <item name="telephone" xsi:type="array">
                                                                    <item name="config" xsi:type="array">
                                                                        <item name="elementTmpl" xsi:type="string">Jeff_CustomCheckout/form/element/my_form_field</item>
                                                                    </item>
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>


                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

Create a UI Component: Jeff_CustomCheckout/js/view/my-step-view


#app/code/Jeff/CustomCheckout/view/frontend/web/js/view/my-step-view.js

define(['ko', 'uiComponent', 'underscore', 'Magento_Checkout/js/model/step-navigator'],
    function(ko, Component, _, stepNavigator) {
        return Component.extend({
            defaults: {
                template: 'Jeff_CustomCheckout/mystep'
            },

            isVisible: ko.observable(true),

            initialize: function() {
                this._super();

                stepNavigator.registerStep(
                    'step_code',
                    null,
                    'Pharmacy Questions',
                    this.isVisible,
                    _.bind(this.navigate, this),
                    5
                /*
                sort order value < 10: before shipping step;
                sort order value < 20: between shipping and payment steps;
                sort order value > 20: after payment step
                */
                );

                return this;
            },
            navigate: function() {

            },

            navigateToNextStep: function() {
                stepNavigator.next();
            }
    });
});

Create the template for new UI Component: Jeff_CustomCheckout/mystep


#app/code/Jeff/CustomCheckout/view/frontend/web/template/mystep.html

<li id="step_code" data-bind="fadeVisible: isVisible">

    <div class="step-title" data-bind="i18n: 'Pharmacy Questions'" data-role="title"></div>

    <div id="checkout-step-title" class="step-content" data-type="content">
        <form data-bind="submit: navigateToNextStep" novalidate="novalidate">
            <div class="custom-checkout">
                <!-- ko foreach: getRegion('field-group-custom') -->
                <!-- ko template: getTemplate() --><!-- /ko -->
                <!-- /ko -->
            </div>

            <hr />
            <br />
            <br />
            <div class="actions-toolbar">
                <div class="primary">
                    <button data-role="opc-continue" type="submit" class="button action continue primary">
                        <span><!-- ko i18n: 'Next' --><!-- /ko --></span>
                    </button>
                </div>
            </div>
        </form>
    </div>

</li>

Create a Plugin for dynamically generating UI components for form fields


#app/code/Jeff/CustomCheckout/Plugin/Checkout/LayoutProcessorPlugin.php

<?php
namespace Jeff\CustomCheckout\Plugin\Checkout;

use Magento\Checkout\Block\Checkout\LayoutProcessor;

class LayoutProcessorPlugin
{
    public function afterProcess(LayoutProcessor $subject, array $jsLayout) 
    {
        $jsLayout['components']['checkout']['children']['steps']['children']['my-new-step']['children']['field-group-custom']['children']['input_custom_field'] = [
            'component' => 'Magento_Ui/js/form/element/abstract',
            'config' => [
                'customScope' => 'customStepFields.input_field',
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/input',
                'id' => 'input_custom_field'
            ],
            'dataScope' => 'customStepFields.imnput_field',
            'label' => 'Input Field:',
            'provider' => 'checkoutProvider',
            'visible' => true,
            'sortOrder' => 1,
            'id' => 'input_field'
        ];

        $jsLayout['components']['checkout']['children']['steps']['children']['my-new-step']['children']['field-group-custom']['children']['custom_checkbox_field'] = [
            'component' => 'Magento_Ui/js/form/element/boolean',
            'config' => [
                'customScope' => 'customStepFields.checkbox_field',
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/checkbox',
                'id' => 'custom_checkbox_field'
            ],
            'dataScope' => 'customStepFields.checkbox_field',
            'label' => 'Checkbox Field:',
            'provider' => 'checkoutProvider',
            'visible' => true,
            'sortOrder' => 2,
            'id' => 'checkbox_field'
        ];

        $jsLayout['components']['checkout']['children']['steps']['children']['my-new-step']['children']['field-group-custom']['children']['custom_select_field'] = [
            'component' => 'Magento_Ui/js/form/element/select',
            'config' => [
                'customScope' => 'customStepFields.select_field',
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/select',
                'id' => 'custom_select_field'
            ],
            'dataScope' => 'customStepFields.select_field',
            'label' => 'Select Field:',
            'options' => $this->getSelectOptions(),
            'caption' => 'please select one',
            'provider' => 'checkoutProvider',
            'visible' => true,
            'sortOrder' => 3,
            'id' => 'select_field'
        ];

        $jsLayout['components']['checkout']['children']['steps']['children']['my-new-step']['children']['field-group-custom']['children']['custom_date_field'] = [
            'component' => 'Magento_Ui/js/form/element/date',
            'config' => [
                'customScope' => 'customStepFields.date_field',
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/date',
                'id' => 'custom_date_field'
            ],
            'dataScope' => 'customStepFields.date_field',
            'label' => 'Date Input Field:',
            'options' => $this->getOptions(),
            'provider' => 'checkoutProvider',
            'visible' => true,
            'sortOrder' => 4,
            'id' => 'date_field'
        ];

        return $jsLayout;
    }

    protected function getOptions()
    {
        return ['dateFormat' => 'm/d/Y', 'timeFormat' => 'HH:mm', 'showsTime' => true];
    }

    protected function getSelectOptions()
    {
        $items = array();

        $items[] = ['value' => '1', 'label' => 'First Class'];
        $items[] = ['value' => '2', 'label' => 'Second Class'];

        return $items;
    }
}

Hook up the plugin with di.xml


<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Checkout\Block\Checkout\LayoutProcessor">
        <plugin name="my_new_step_to_checkout" type="Jeff\CustomCheckout\Plugin\Checkout\LayoutProcessorPlugin" sortOrder="20" />
    </type>
</config>

Some images for final results:

custom linked product


custom linked product


The source code can be downloaded at My Github .

If you have any question, please let me know. Enjoy!


comments powered by Disqus