7

I'm doing Custom Checkout, and here is what i've done til now. enter image description here

The adea is to use validate so when i click on "Next" button, it gonna check if the checkbox in "Terms of Use", if its checked, then it free to go, but if its not, a validate message will appear.

Here is my code, i've push to github so you guys can easily to watch and re-procedure:
https://github.com/saxsax1995/MagentoCheckoutCustom

So i've follow devdoc and mageplaza
https://devdocs.magento.com/guides/v2.2/howdoi/checkout/checkout_carrier.html https://www.mageplaza.com/custom-shipping-carrier-validator-magento-2.html
But it still doesn't work.

I've try to console.log() in C:\xampp\htdocs\magento\app\code\Aht\MagentoCheckoutCustom\view\frontend\web\js\view\custom-validation.js but it doesn't run to it.
Do i missing something here? I think i've do everything the devdoc and mageplaza said.

So here is my code:

C:\xampp\htdocs\magento\app\code\Aht\MagentoCheckoutCustom\view\frontend\layout\checkout_index_index.xml

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="checkout"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <css src="Aht_MagentoCheckoutCustom/css/style.css"/>
        <!--<script src="Aht_MagentoCheckoutCustom::js/custom-validator.js"></script>-->
    </head>
    <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="step-config" xsi:type="array">
                                                    <item name="children" xsi:type="array">
                                                        <item name="shipping-rates-validation" xsi:type="array">
                                                            <item name="children" xsi:type="array">
                                                                <item name="custom-validator" xsi:type="array">
                                                                    <item name="component" xsi:type="string">Aht_MagentoCheckoutCustom/js/view/custom-validation</item>
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                                <item name="shippingAddress" xsi:type="array">
                                                    <!--<item name="config" xsi:type="array">-->
                                                        <!--<item name="template" xsi:type="string">Aht_MagentoCheckoutCustom/custom-shipping</item>-->
                                                    <!--</item>-->
                                                    <item name="children" xsi:type="array">
                                                        <item name="shipping-address-fieldset" xsi:type="array">
                                                            <item name="children" xsi:type="array">
                                                                <item name="delivery-group" xsi:type="array">
                                                                    <item name="component" xsi:type="string">Aht_MagentoCheckoutCustom/js/view/custom-shipping</item>
                                                                    <item name="provider" xsi:type="string">checkoutProvider</item>
                                                                    <item name="sortOrder" xsi:type="string">150</item>
                                                                    <item name="children" xsi:type="array">
                                                                        <item name="delivery-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>
                                                                            <item name="displayArea" xsi:type="string">delivery-fieldsets</item>
                                                                            <item name="children" xsi:type="array">
                                                                                <item name="delivery_instruction" 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 (e.g. they can be validated separately)-->
                                                                                        <item name="customScope" xsi:type="string">deliveryForm</item>
                                                                                        <item name="template" xsi:type="string">ui/form/field</item>
                                                                                        <item name="elementTmpl" xsi:type="string">ui/form/element/input</item>
                                                                                    </item>
                                                                                    <!-- value element allows to specify default value of the form field -->
                                                                                    <!--<item name="value" xsi:type="string">Yout value here</item>-->
                                                                                    <item name="provider" xsi:type="string">checkoutProvider</item>
                                                                                    <item name="dataScope" xsi:type="string">deliveryForm.delivery_instruction</item>
                                                                                    <item name="label" xsi:type="string">Delivery Instruction:</item>
                                                                                    <item name="sortOrder" xsi:type="string">1</item>
                                                                                </item>
                                                                                <item name="delivery_type" xsi:type="array">
                                                                                    <item name="component" xsi:type="string">Magento_Ui/js/form/element/select</item>
                                                                                    <item name="config" xsi:type="array">
                                                                                        <!--customScope is used to group elements within a single form (e.g. they can be validated separately)-->
                                                                                        <item name="customScope" xsi:type="string">deliveryForm</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">Delivery Type 1</item>
                                                                                            <item name="value" xsi:type="string">delivery_type_1</item>
                                                                                        </item>
                                                                                        <item name="1" xsi:type="array">
                                                                                            <item name="label" xsi:type="string">Delivery Type 2</item>
                                                                                            <item name="value" xsi:type="string">delivery_type_2</item>
                                                                                        </item>
                                                                                        <item name="2" xsi:type="array">
                                                                                            <item name="label" xsi:type="string">Delivery Type 3</item>
                                                                                            <item name="value" xsi:type="string">delivery_type_3</item>
                                                                                        </item>
                                                                                    </item>
                                                                                    <!-- value element allows to specify default value of the form field -->
                                                                                    <item name="provider" xsi:type="string">checkoutProvider</item>
                                                                                    <item name="dataScope" xsi:type="string">deliveryForm.delivery_type</item>
                                                                                    <item name="label" xsi:type="string">Delivery Type:</item>
                                                                                    <item name="sortOrder" xsi:type="string">2</item>
                                                                                </item>
                                                                                <item name="term_of_use" xsi:type="array">
                                                                                    <item name="component" xsi:type="string">Magento_Ui/js/form/element/boolean</item>
                                                                                    <item name="config" xsi:type="array">
                                                                                        <!--customScope is used to group elements within a single form (e.g. they can be validated separately)-->
                                                                                        <item name="customScope" xsi:type="string">deliveryForm</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">deliveryForm.term_of_use</item>
                                                                                    <item name="description" xsi:type="string">I accept the Terms of Use and Privacy Policy
                                                                                        Sorry, you must accept our Terms of Use and our Privacy Policy before placing your order</item>
                                                                                    <item name="sortOrder" xsi:type="string">3</item>
                                                                                </item>
                                                                            </item>
                                                                        </item>
                                                                    </item>
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

C:\xampp\htdocs\magento\app\code\Aht\MagentoCheckoutCustom\view\frontend\web\js\model\custom-shipping-rates-validation-rules.js

define(
    [],
    function () {
        'use strict';
        return {
            getRules: function() {
                return {
                    'term_of_use': {
                        'required': true
                    }
                };
            }
        };
    }
)

C:\xampp\htdocs\magento\app\code\Aht\MagentoCheckoutCustom\view\frontend\web\js\model\custom-validator.js

define(
    [
        'jquery',
        'mageUtils',

        './custom-shipping-rates-validation-rules',
        'mage/translate'
    ],
    function ($, utils, validationRules, $t) {
        'use strict';
        return {
            validationErrors: [],
            validate: function (address) {
                var self = this;
                this.validationErrors = [];
                $.each(validationRules.getRules(), function (field, rule) {
                    if (rule.required && utils.isEmpty(address[field])) {
                        var message = $t('Field ') + field + $t(' is required.');
                        self.validationErrors.push(message);
                    }
                });
                return !Boolean(this.validationErrors.length);
            }
        };
    });

C:\xampp\htdocs\magento\app\code\Aht\MagentoCheckoutCustom\view\frontend\web\js\view\custom-validation.js

define(
    [
        'uiComponent',
        'Magento_Checkout/js/model/shipping-rates-validator',
        'Magento_Checkout/js/model/shipping-rates-validation-rules',
        'Aht_MagentoCheckoutCustom/js/model/custom-validator',
        'Aht_MagentoCheckoutCustom/js/model/custom-shipping-rates-validation-rules'
    ],
    function (Component,
              defaultShippingRatesValidator,
              defaultShippingRatesValidationRules,
              shippingRatesValidator,
              shippingRatesValidationRules)
    {
        'use strict';
        console.log('123');
        defaultShippingRatesValidator.registerValidator('custom-validator', shippingRatesValidator);
        defaultShippingRatesValidationRules.registerRules('custom-shipping-rates-validation-rules', shippingRatesValidationRules);
        return Component;
    }
);



In the source, it doesn't even called in my module.
Here is description image: enter image description here


Please give advice, thanks for reading :)

EDIT 1:
After few days looking for solution, look like custom validate for shipping address doesn't work this way.
I've found another solution, follow the submit form event when we click "Next" in setShippingInformation -> validateShippingInformation function in shipping.js enter image description here

C:\xampp\htdocs\magento\pub\static\frontend\Magento\luma\en_US\Magento_Checkout\js\view\shipping.js


What i'm trying to do is validate the "term of use", and then show validate message after it. Right now, it console.log correctly when i click on "Next" button, but doesn't show correct message. Its always show "false" just like my description image.
So here is what i've done. (i've updated on github if you guys want to see the module)

C:\xampp\htdocs\magento\app\code\Aht\MagentoCheckoutCustom\view\frontend\web\js\view\shipping.js

define([
   'jquery',
    'ko'
], function ($, ko) {
    'use strict';

    return function (Target) {
        return Target.extend({
            errorDeliveryValidationMessage: ko.observable(false),
            validateShippingInformation: function () {
                this._super();
                if($('[name="term_of_use"]:checked').length > 0) {
                    console.log('1');
                    return true;
                }
                console.log('2');
                this.errorDeliveryValidationMessage('Please agree our term of use.');
                return false;
            }
        });
    }
});

C:\xampp\htdocs\magento\app\code\Aht\MagentoCheckoutCustom\view\frontend\requirejs-config.js

var config = {
    map: {
        '*': {
            'Magento_Checkout/js/model/shipping-save-processor/default': 'Aht_MagentoCheckoutCustom/js/model/shipping-save-processor/default'
        }
    }
    ,
    'config': {
        'mixins': {
            'Magento_Checkout/js/view/shipping': {
                'Aht_MagentoCheckoutCustom/js/view/shipping': true
            }
        }
    }
};

C:\xampp\htdocs\magento\pub\static\frontend\Magento\luma\en_US\Aht_MagentoCheckoutCustom\template\shipping-delivery\delivery-form.html

<form class="form form-shipping-delivery" id="co-shipping-delivery-form" data-bind="attr: {'data-hasrequired': $t('* Required Fields')}">
    <div id="shipping-delivery" class="fieldset delivery">
        <!-- ko foreach: getRegion('delivery-fieldsets') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!--/ko-->
    </div>
</form>
<div role="alert" class="message notice">
    <span text="errorDeliveryValidationMessage()" />
</div>

3 Answers 3

0

You can try something like this , add this in your layout checkout_index_index.xml

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
 -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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="billing-step" xsi:type="array">
                                        <item name="children" xsi:type="array">
                                            <item name="payment" xsi:type="array">
                                                <item name="children" xsi:type="array">
                                                    <item name="payments-list" xsi:type="array">
                                                        <item name="children" xsi:type="array">
                                                            <item name="before-place-order" xsi:type="array">
                                                                <item name="children" xsi:type="array">
                                                                    <item name="agreements" xsi:type="array">
                                                                        <item name="component" xsi:type="string">Aht_MagentoCheckoutCustom/js/view/custom-js</item>
                                                                        <item name="sortOrder" xsi:type="string">100</item>
                                                                        <item name="displayArea" xsi:type="string">before-place-order</item>
                                                                        <item name="dataScope" xsi:type="string">checkoutAgreements</item>
                                                                        <item name="provider" xsi:type="string">checkoutProvider</item>
                                                                    </item>
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </item>
            </argument>
        </arguments>
    </referenceBlock>
</body>

in Aht\MagentoCheckoutCustom\view\frontend\web\js\view\custom-js.js

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

 define([
'ko',
'jquery',
'uiComponent',
'Magento_CheckoutAgreements/js/model/agreements-modal'
], function (ko, $, Component, agreementsModal) {
//put the logic here
});
6
  • Unfortunately, Its doesn't work :( Do you have any other solutions?
    – fudu
    Sep 20, 2018 at 1:40
  • I think my problems here is the "click event" doesn't work, i've use deps:[ 'Aht_MagentoCheckoutCustom/js/custom-js' ] in requirejs-config.js so it can import to every page in magento 2, and console.log works. But the click event doesn't work.
    – fudu
    Sep 20, 2018 at 2:08
  • I've follow this post magento.stackexchange.com/questions/131056/…
    – fudu
    Sep 20, 2018 at 2:10
  • Seem like it call only at loading page maybe ?
    – fudu
    Sep 20, 2018 at 2:13
  • I've edit the post, please check :)
    – fudu
    Sep 22, 2018 at 8:42
0

You can use magento-init for init and load your custom Js component. As following:

Step 1: remove js extension in requirejs-config.js

var config = {
    map: {
        '*': {
            //customJS doesn't work because no where need to use that customJS.
            customJs : 'Aht_MagentoCheckoutCustom/js/custom-js',
            'Magento_Checkout/js/model/shipping-save-processor/default': 'Aht_MagentoCheckoutCustom/js/model/shipping-save-processor/default'
        }
    }
};

Step 2: Add template file (use for init js file) to checkout_index_index.xml layout.

<referenceContainer name="content">
    <block class="Magento\Framework\View\Element\Template" name="custom-js" template="Aht_MagentoCheckoutCustom::js.phtml" after="-"/>
</referenceContainer>

And remove import js on head

<script src="Aht_MagentoCheckoutCustom::js/custom-js.js"></script>

Step 3: create js.phtml in view/frontend/templates and put content

<script type="text/x-magento-init">
    {
        "*":{
            "customJs":{}
        }
    }
</script>

Step 4: change content of your js file (custom-js.js).

define([
    'jquery'
], function ($) {
    'use strict';

    $.fn.customJs = function(options) {
        console.log('1234');
    };

    return true;
});
5
  • wait, can we use <referenceContainer name="content"> to create our new block and template ? I mean its already used?
    – fudu
    Sep 21, 2018 at 2:55
  • Add to checkout_index_index.xml . Example gist.github.com/bangnguyen47/… Sep 21, 2018 at 3:00
  • I've try your solution, it does work, but still, my checkout page still not working. Just like my old way... :( Both solution still doesn't work on checkout page (first step).
    – fudu
    Sep 22, 2018 at 3:18
  • I've edit the post, please check :)
    – fudu
    Sep 22, 2018 at 8:42
  • I've edit the post, please check :) EDIT 1
    – fudu
    Sep 27, 2018 at 14:09
0

The reason your custom-validation.js file was not loading is due to your XML configuration.

C:\xampp\htdocs\magento\app\code\Aht\MagentoCheckoutCustom\view\frontend\layout\checkout_index_index.xml

In this file you have named your value incorrectly here:

<item name="custom-validator" xsi:type="array">
    <item name="component" xsi:type="string">Aht_MagentoCheckoutCustom/js/view/custom-validation</item>
</item>

custom-validator is what is incorrect. It should match the following naming convention: {shippingMethodCode}-rates-validation, replacing {shippingMethodCode} with the exact value you have chosen for your shipping method in your module's etc/config.xml file. So For example if you have the following:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <carriers>
            <foo_bar>
                <active>1</active>
                <title>Shipping</title>
                <shipping_cost>0.00</shipping_cost>
                <showmethod>yes</showmethod>
                <model>Aht\MagentoCheckoutCustom\Model\Carrier\Foobar</model>
            </foo_bar>
        </carriers>
    </default>
</config>

Then your layout XML would be:

<item name="foo_bar-rates-validation" xsi:type="array">
    <item name="component" xsi:type="string">Aht_MagentoCheckoutCustom/js/view/custom-validation</item>
</item>

The reason for this can be found in (referencing Magento 2.3.2 at time of writing) Magento\Checkout\Block\Checkout\LayoutProcessor::processShippingChildrenComponents().

private function processShippingChildrenComponents($shippingRatesLayout)
{
    $activeCarriers = $this->shippingConfig->getActiveCarriers(
        $this->storeManager->getStore()->getId()
    );
    foreach (array_keys($shippingRatesLayout) as $carrierName) {
        $carrierKey = str_replace('-rates-validation', '', $carrierName);
        if (!array_key_exists($carrierKey, $activeCarriers)) {
            unset($shippingRatesLayout[$carrierName]);
        }
    }
    return $shippingRatesLayout;
}

Here we can see Magento is comparing the active shipping carriers to the UI components added via layout XML. It is using the $carrierName (the value from our checkout_index_index.xml file) and stripping the hard coded string of '-rates-validation' from the name of the from UI component name to come up with a $carrierKey. Then comparing the carrier key to the array of active shipping carrier codes (which we add in our etc/config.xml), to determine if it should remove this layout update instruction or not.

v2.3 Dev Docs on this subject: https://devdocs.magento.com/guides/v2.3/howdoi/checkout/checkout_carrier.html

Furthermore, once you have custom-validation.js being loaded on the page, I'd recommend changing the contents of it to again have values match your carrier code.

C:\xampp\htdocs\magento\app\code\Aht\MagentoCheckoutCustom\view\frontend\web\js\view\custom-validation.js

On these lines:

defaultShippingRatesValidator.registerValidator('custom-validator', shippingRatesValidator);
defaultShippingRatesValidationRules.registerRules('custom-shipping-rates-validation-rules', shippingRatesValidationRules);

The strings you're passing here, Magento once again utilizes in a similar fashion as the layout XML. Comparing active carriers and determining if it should add the rules to the set of "Observable Fields". We'll find this in

vendor/magento/module-checkout/view/frontend/web/js/model/shipping-rates-validator.js

(Lines 52-56 of Magento 2.3.2)

registerValidator: function (carrier, validator) {
    if (checkoutConfig.activeCarriers.indexOf(carrier) !== -1) {
        validators.push(validator);
    }
},

carrier here, is the string you've passed into registerValidator(), and activeCarriers is an array of the active shipping carriers. So if you don't pass in the string of your carrier code you've defined, you won't be able to add validation rules for it.

We'll find this same logic in the registerRules() method.

vendor/magento/module-checkout/view/frontend/web/js/model/shipping-rates-validation-rules.js

registerRules: function (carrier, rules) {
    if (checkoutConfig.activeCarriers.indexOf(carrier) !== -1) {
        ratesRules[carrier] = rules.getRules();
    }
},

Hence, the strings passed to registerRules() and registerValidator() in your custom-validation.js should the same (which they are not, so that tells me at least one does not exactly match your carrier code).

2
  • I have made all those steps, and my validator and rules are registered, but on validate only the default validator is ran (since it's using Array.some to validate and that one returns true). Am I missing something?
    – Einacio
    Jan 21, 2020 at 12:57
  • Is there any place (maybe a github gist or another service) that you can provide example code? There's a lot of "need to be typed out exactly correct" aspects of this subject so it is hard to know what may be going wrong for you without seeing any code. Jan 24, 2020 at 13:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.