Magento 2 - How to add a custom field to checkout and then send it

All tutorials are covering only adding a fields, but saving value of this fileds is skipped #mindblown. I don't know why, it's most important part of adding any field or form.

I tried to follow Magento docs, but... it sucks.

For testing purposes I try to add another fields to shipping address, just to ignore custom scopes, custom data sets, custom data providers and other undocumented stuff, which looks too weird for me.

I have no idea what mean that form is "static" or "dynamic". For me all checkout forms are builded dynamically on top of KnockoutJS templates, but... when I try "static" way, I'm able to add inputs here (so it's a static form or not?).

First I try to debug why Knockout observables just ignore my fields during parsing and sending data. I found that my fields have empty name parameter, but I can't manage a way to fix this issue. IMO it should be passed to UI component renderer via inputName parameter, same as any other options like disabled, placeholder etc. (other parameters works fine, I checked configuration generated from my XML to checkout module initialization and looks good for me)

Second I tried to use "dynamic" way with creating plugin with LayoutProcessor and passing exactly same data... and now I have fields with names, but sending still doesn't work at all.

After digging into JS I found that preparing this request is maintained in module-checkout/view/frontend/web/js/model/shipping-save-processor/default.js file, which depends on module-checkout/view/frontend/web/js/model/quote.js where Knockout observables are defined/created.

Somehow module-checkout/view/frontend/web/js/model/address-converter.js update this observables and depends on module-checkout/view/frontend/web/js/model/new-customer-address.js, where I finally found some interesting configuration options - list of all address fields.

When I add my fields here, scripts start parsing and sending them, OFC I get 500, b/c backend doesn't recognize them... (don't ask, I'm not a backend dev)

So here comes my questions:

  • It's a right way to handle this type of customization? (b/c looks weird for me)
  • How to send values of fields not related to new addresses? I didn't see similar configuration anywhere. In my case I'd like to send order comment (textarea) and invoice request (checkbox). Both shouldn't be saved as a address, b/c some users might want to save this address for future use.
  • Is there any documentation about "static" and "dynamic" forms or some examples / comparison? It's worth thinking about it this way?

Additional existential question:

  • Why this is so inconsistent? Why I have to define tons of parameters in XML/PHP files, while Magento at all can only render a input and then I have to handle everything on my own?

Solutions

In my experience, m2 use xml to define components such as fields etc.. It is help for debugging, interactive with data more easier. On field of front-enders you should try to override checkout templates and add your own custom fields in there. With K.O helps you ability to work and bind data from frontend and backend

I will try to answer your question(s).

  1. No. This is not a correct way to add custom attributes to the shipping address form. You do not need to edit new-customer-address.js. Indeed, this JS file lists all predefined address attributes and matches corresponding backend interface \Magento\Quote\Api\Data\AddressInterface but Magento provides ability to pass any custom attributes to backend without modification of the backend/frontend components.

    Mentioned JS component has customAttributes property. Your custom attributes will be automatically handled if their $dataScopePrefix is 'shippindAddress.custom_attributes'.

  2. If I understood your question correctly, you have data that is not a part of customer address but you need to send it to backend as well. The answer to this question is:

    It depends. For instance, you can choose the following approach: add custom form to checkout page that includes all your extra fields (like comment, invoice request etc), add JS logic that will handle this form based on some events, and provide a custom service that will receive the data from frontend and store it somewhere for future use.

  3. All official documentation related to checkout is located at http://devdocs.magento.com/guides/v2.1/howdoi/checkout/checkout_overview.html. The term static refers to the forms where all the fields are already known/predefined (for instance: form will always have 2 text fields with predefined labels) and cannot change based on some settings in the backend.

    Such forms can be declared using layout XML configuration. On the other hand, term dynamic refers to forms whose field set can change (for instance: checkout form can have more/less fields based on configuration settings).

    In this case the only way to declare such form is to use LayoutProcessor plugin.

  4. :) Magento tries to cover as much possible use cases that can be significant to the merchants during Magento use/customization as possible. Sometimes this leads to situation when some simple use cases become more complex.

Hope, this helps.

=========================================================================

OK... Lets write some code ;)

  1. PHP code that inserts additional field in LayoutProcessor

========

/**
 * @author aakimov
 */
$customAttributeCode = 'custom_field';
$customField = [
    'component' => 'Magento_Ui/js/form/element/abstract',
    'config' => [
        // customScope is used to group elements within a single form (e.g. they can be validated separately)
        'customScope' => 'shippingAddress.custom_attributes',
        'customEntry' => null,
        'template' => 'ui/form/field',
        'elementTmpl' => 'ui/form/element/input',
        'tooltip' => [
            'description' => 'Yes, this works. I tested it. Sacrificed my lunch break but verified this approach.',
        ],
    ],
    'dataScope' => 'shippingAddress.custom_attributes' . '.' . $customAttributeCode,
    'label' => 'Custom Attribute',
    'provider' => 'checkoutProvider',
    'sortOrder' => 0,
    'validation' => [
       'required-entry' => true
    ],
    'options' => [],
    'filterBy' => null,
    'customEntry' => null,
    'visible' => true,
];

$jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset']['children'][$customAttributeCode] = $customField;

return $jsLayout;

As I already mentioned, this will add your field to customAttributes property of the JS address object. This property was designed to contain custom EAV address attributes and is related to \Magento\Quote\Model\Quote\Address\CustomAttributeListInterface::getAttributes method.

The code above will automatically handle local storage persistence on frontend. You can get your field value from local storage using checkoutData.getShippingAddressFromData() (where checkoutData is Magento_Checkout/js/checkout-data).

  1. Add mixin to change the behavior of 'Magento_Checkout/js/action/set-shipping-information' (this component is responsible for data submission between shipping and billing checkout steps)

========

2.1. Create your_module_name/view/frontend/requirejs-config.js


/**
 * @author aakimov
 */
var config = {
    config: {
        mixins: {
            'Magento_Checkout/js/action/set-shipping-information': {
                '<your_module_name>/js/action/set-shipping-information-mixin': true
            }
        }
    }
};

2.2. Create your_module_name/view/frontend/web/js/action/set-shipping-information-mixin.js


/**
 * @author aakimov
 */
/*jshint browser:true jquery:true*/
/*global alert*/
define([
    'jquery',
    'mage/utils/wrapper',
    'Magento_Checkout/js/model/quote'
], function ($, wrapper, quote) {
    'use strict';

    return function (setShippingInformationAction) {

        return wrapper.wrap(setShippingInformationAction, function (originalAction) {
            var shippingAddress = quote.shippingAddress();
            if (shippingAddress['extension_attributes'] === undefined) {
                shippingAddress['extension_attributes'] = {};
            }

            // you can extract value of extension attribute from any place (in this example I use customAttributes approach)
            shippingAddress['extension_attributes']['custom_field'] = shippingAddress.customAttributes['custom_field'];
            // pass execution to original action ('Magento_Checkout/js/action/set-shipping-information')
            return originalAction();
        });
    };
});
  1. Create your_module_name/etc/extension_attributes.xml

========

<?xml version="1.0"?>
<!--
/**
 * @author aakimov
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Quote\Api\Data\AddressInterface">
        <attribute code="custom_field" type="string" />
    </extension_attributes>
</config>

This will add an extension attribute to address model on backend side. Extension attributes are one of the extension points that Magento provides. To access your data on backend you can use:

// Magento will generate interface that includes your custom attribute
$value = $address->getExtensionAttributes()->getCustomField();

Hope, this helps and will be added to official documentation.

Similar questions

how to add custom attributes in the edit form and then save them
I have created 4 custom attributes attributes and i can see them in the register.phtml and i can create new customers but my question how to show those attributes in the edit account like first and last name and how to save them when submitting the form any help please Update here is the code that i used to create my ncustom attributes:
Add product to cart then auto redirect to checkout page
I need some change in website. When product add to cart then page auto redirect to checkout page . Magento default option show only for auto redirect cart page but I need redirect to checkout page
Customize "apply coupon" action when a particular coupon is applied and then re-update shipping in cart & checkout
I am running magento ver. 1.9.0.1 in my site. I want to customize shipping rate when a particular coupon is applied. How can I do that when "Apply Coupon" button is clicked, if a given coupon is the one, that is applied ? I never had much encounter with code in price rule section so I am not aware of which controller is executed when "Apply Coupon"...
How to set up a custom variable in custom Adminhtml form and then parse it to the Observer
I am working on a custom Magento extension. Version: 1.9.0.1. I have a custom Adminhtml form, here it is: Here is the Form code: I use this code to get the customer name from the order: How can i check the whole text and if it finds CustomVariable_CustomerName to replace it with the real customer name ? Thanks in advance!
PHP
Magento if product option selected then open text field
i want to create a product (shirt) which can be tailored. The customer would click the option to tailor, adding cost of tailoring to price and a text field would open letting the customer enter their length, width, etc. I am able to do the first part when I customize the product, but I need it to be linked to the text input from the customer lettin...
Magento - Can I send packing slip as email when order is placed and send together with order mail
when an order is placed the client receives an email and the order department gets a copy. The department would also like to be sent a packing slip as email straight away instead of having to print a pdf. So apart form the fact that I'd have to make an HTML template for the packing slip, how could I get Magento to send an other email AND use an oth...

Also ask

We use cookies to deliver the best possible experience on our website. By continuing to use this site, accepting or closing this box, you consent to our use of cookies. To learn more, visit our privacy policy.