1

Following this post

Magento 2 : How to display Shipping Rates on a Product Page

i can get shipping rates for current product, tested with curl:

curl -ik -X POST -H "Content-Type: application/json" -d "{""address"":{""country_id"": ""IT""}}" https://example.com/rest/V1/shipping/estimate/51003PRI

got the following response:

[{"carrier_code":"tablerate","method_code":"bestway","carrier_title":"Shipping Method","method_title":"Express Delivery","amount":0,"base_amount":0,"available":true,"error_message":"","price_excl_tax":0,"price_incl_tax":0}]

Now i need to display a form in product page like the "Estimate Shipping and Tax" in cart page to pass the address in post action.

I would like to do it using ui form and ajax and need to update region field depending country field.

Any suggestions?

EDIT

I got the form in product page with the following:

[vendor][module]\Block\Produt\View\Shipping:

<?php
namespace [vendor]\[module]\Block\Product\View;

class Shipping extends \Magento\Catalog\Block\Product\View\AbstractView
{
    protected $_customerSession;
    
    /**
     * @param \Magento\Catalog\Block\Product\Context $context
     * @param \Magento\Framework\Stdlib\ArrayUtils $arrayUtils
     * @param array $layoutProcessors
     * @param array $data
     */
    public function __construct(
        \Magento\Catalog\Block\Product\Context $context,
        \Magento\Framework\Stdlib\ArrayUtils $arrayUtils,
        \Magento\Framework\App\Request\Http $request,
        \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository,
        \Magento\Customer\Model\SessionFactory $customerSession,
        array $layoutProcessors = [],
        array $data = []
    ) {
        $this->request = $request;
        $this->customerRepository = $customerRepository;
        $this->_customerSession = $customerSession;
        $this->layoutProcessors = $layoutProcessors;
        parent::__construct(
            $context,
            $arrayUtils,
            $data
        );
    }

    /**
     * Retrieve serialized JS layout configuration ready to use in template
     *
     * @return string
     */
    public function getJsLayout()
    {
        foreach ($this->layoutProcessors as $processor) {
            $this->jsLayout = $processor->process($this->jsLayout);
        }

        return json_encode($this->jsLayout, JSON_HEX_TAG);
    }
    
    public function getCurrentCustomer()
    {
        $customer = $this->_customerSession->create();
        
        return $customer->getDefaultTaxShippingAddress();
    }

    public function getSerializedCurrentCustomer()
    {
        return json_encode($this->getCurrentCustomer(), JSON_HEX_TAG);
    }
}

[vendor][module]\view\frontend\layout\catalog_product_view.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <referenceContainer name="product.info.main">
        <block class="[vendor]\[module]\Block\Product\View\Shipping" name="estimate.product.shipping" template="[vendor]_[module]::product/view/shipping.phtml" after="checkout.cart.summary.title">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="types" xsi:type="array">
                        <item name="form.input" xsi:type="array">
                            <item name="component" xsi:type="string">Magento_Ui/js/form/element/abstract</item>
                            <item name="config" xsi:type="array">
                                <item name="provider" xsi:type="string">checkoutProvider</item>
                                <item name="deps" xsi:type="array">
                                    <item name="0" xsi:type="string">checkoutProvider</item>
                                </item>
                                <item name="template" xsi:type="string">ui/form/field</item>
                                <item name="elementTmpl" xsi:type="string">ui/form/element/input</item>
                            </item>
                        </item>
                    </item>
                    <item name="components" xsi:type="array">
                        <item name="block-summary" xsi:type="array">
                            <item name="component" xsi:type="string">uiComponent</item>
                            <item name="children" xsi:type="array">
                                <item name="block-shipping" xsi:type="array">
                                    <item name="component" xsi:type="string">[vendor]_[module]/js/product/view/shipping-estimation</item>
                                    <item name="provider" xsi:type="string">checkoutProvider</item>
                                    <item name="sortOrder" xsi:type="string">1</item>
                                    <item name="deps" xsi:type="array">
                                        <item name="0" xsi:type="string">block-summary.block-shipping.address-fieldsets</item>
                                    </item>
                                    <item name="children" xsi:type="array">
                                        <item name="address-fieldsets" xsi:type="array">
                                            <item name="component" xsi:type="string">uiComponent</item>
                                            <item name="config" xsi:type="array">
                                                <item name="deps" xsi:type="array">
                                                    <item name="0" xsi:type="string">checkoutProvider</item>
                                                </item>
                                            </item>
                                            <item name="displayArea" xsi:type="string">address-fieldsets</item>
                                            <item name="children" xsi:type="array">
                                                <item name="city" xsi:type="array">
                                                    <item name="sortOrder" xsi:type="string">115</item>
                                                    <item name="dataScope" xsi:type="string">shippingAddress.city</item>
                                                    <item name="provider" xsi:type="string">checkoutProvider</item>
                                                </item>
                                                <item name="country_id" xsi:type="array">
                                                    <item name="sortOrder" xsi:type="string">110</item>
                                                    <item name="dataScope" xsi:type="string">shippingAddress.country_id</item>
                                                    <item name="provider" xsi:type="string">checkoutProvider</item>
                                                </item>
                                                <item name="region_id" xsi:type="array">
                                                    <item name="component" xsi:type="string">Magento_Ui/js/form/element/region</item>
                                                    <item name="sortOrder" xsi:type="string">112</item>
                                                    <item name="config" xsi:type="array">
                                                        <item name="template" xsi:type="string">ui/form/field</item>
                                                        <item name="elementTmpl" xsi:type="string">ui/form/element/select</item>
                                                        <item name="customEntry" xsi:type="string">shippingAddress.region</item>
                                                    </item>
                                                    <!-- Value of region_id field is filtered by the value of county_id attribute -->
                                                    <item name="filterBy" xsi:type="array">
                                                        <item name="target" xsi:type="string">
                                                            <![CDATA[${ $.provider }:${ $.parentScope }.country_id]]>
                                                        </item>
                                                        <item name="field" xsi:type="string">country_id</item>
                                                    </item>
                                                </item>
                                                <!-- The following items override configuration of corresponding address attributes -->
                                                <item name="region" xsi:type="array"> 
                                                    <!-- Make region attribute invisible on frontend. Corresponding input element is created by region_id field -->
                                                    <item name="visible" xsi:type="boolean">false</item>
                                                    <item name="component" xsi:type="string">Magento_Ui/js/form/element/abstract</item>
                                                    <item name="sortOrder" xsi:type="string">111</item>
                                                    <item name="dataScope" xsi:type="string">shippingAddress.region</item>
                                                </item>
                                                <item name="postcode" xsi:type="array"> 
                                                    <!-- post-code field has custom UI component -->
                                                    <item name="component" xsi:type="string">Magento_Ui/js/form/element/abstract</item>
                                                    <item name="dataScope" xsi:type="string">shippingAddress.postcode</item>
                                                    <item name="sortOrder" xsi:type="string">114</item>
                                                    <item name="provider" xsi:type="string">checkoutProvider</item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                        <item name="checkoutProvider" xsi:type="array">
                            <item name="component" xsi:type="string">uiComponent</item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </block>
    </referenceContainer>
</page>

[vendor][module]\view\frontend\templates\product\view\shipping.phtml

<?php
/** @var [vendor]\[module]\Block\Product\View\Shipping */
$product = $block->getProduct();
?>
<?php if ($product->getTypeId() != 'virtual' ) : ?>
<div id="block-shipping"
     class="block shipping"
     data-mage-init='{"collapsible":{"openedState": "active", "saveState": true}}'
>
    <div class="title" data-role="title">
        <strong id="block-shipping-heading" role="heading" aria-level="2">
            <?= $block->escapeHtml(__('Estimate Shipping Cost')) ?>
        </strong>
    </div>
    <div id="block-summary"
         data-bind="scope:'block-summary'"
         class="content"
         data-role="content"
         aria-labelledby="block-shipping-heading"
    >
        <!-- ko template: getTemplate() --><!-- /ko -->
        <script type="text/x-magento-init">
            {
                "#block-summary": {
                    "Magento_Ui/js/core/app": <?= /* @noEscape */ $block->getJsLayout() ?>
                }
            }
        </script>
        <script>
            window.customerAddress = <?= /* @noEscape */ $block->getSerializedCurrentCustomer() ?>;
            require([
                'mage/url',
                'Magento_Ui/js/block-loader'
            ], function(url, blockLoader) {
                blockLoader(
                    "<?= $block->escapeJs($block->escapeUrl($block->getViewFileUrl('images/loader-1.gif'))) ?>"
                );
                return url.setBaseUrl('<?= $block->escapeJs($block->escapeUrl($block->getBaseUrl())) ?>');
            })
        </script>
    </div>
</div>
<?php endif; ?>

[vendor][module]\view\frontend\web\js\product\view\shipping-estimation.js

define(
    [
        'jquery',
        'ko',
        'Magento_Ui/js/form/form',
        'uiRegistry',
        'mage/storage',
        'mage/validation'
    ],
    function (
        $,
        ko,
        Component,
        registry,
        storage
    ) {
        'use strict';

        return Component.extend({
            defaults: {
                template: '[vendor]_[module]/product/view/shipping-estimation'
            },
            
            /**
             * @override
             */
            initialize: function () {
                this._super();
                
                var self = this;
                                
                registry.async('checkoutProvider')(function (checkoutProvider) {
                    var address, estimatedAddress;
                    
                    address = window.customerAddress;

                    if (address) {
                        estimatedAddress = 
                            {
                                // only the following fields must be used by estimation form data provider
                                'country_id': address.country_id,
                                region: address.region,
                                'region_id': address.region_id,
                                postcode: address.postcode
                            };
                        checkoutProvider.set(
                            'shippingAddress',
                            $.extend({}, checkoutProvider.get('shippingAddress'), estimatedAddress)
                        );
                        
                        self.getRates(address);
                    }
                });

                return this;
            },
            
            initElement: function (element) {
                this._super();
                
                var self = this,
                    checkoutProvider = registry.get('checkoutProvider');
                
                if (element.index === 'address-fieldsets') {
                    setTimeout(function () {
                        $.each(element.elems(), function (index, elem) {
                             elem.on('value', function () {
                                clearTimeout(self.validateAddressTimeout);
                                self.validateAddressTimeout = setTimeout(function () {
                                    self.getRates(checkoutProvider.shippingAddress);
                                }, 1000);
                             });
                        });
                    },1000);
                }
            },
            
            getRates: function (address){
                var self = this,
                    productId = window.productId,
                    url = 'rest/V1/shipping/estimate/' + productId;
                
                self.shippingRates = ko.observableArray([]);
                
                storage.post(
                    url,
                    JSON.stringify({
                       'address': address
                    }),
                    false
                ).done(function (result) {
                    self.shippingRates(result);
                }).fail(function (response) {
                });
            }

        });
    }
);

[vendor][module]\view\frontend\web\template\product\view\shipping-estimation.html

<form method="post" id="shipping-zip-form">
    <fieldset class="fieldset estimate">
        <legend class="legend">
            <span data-bind="text: $t('Estimate Shipping Cost') "></span>
        </legend><br>
        <p class="field note" data-bind="text: $t('Enter your destination to get a shipping estimate.')"></p>
        <!-- ko foreach: getRegion('address-fieldsets') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!--/ko-->
    </fieldset>
</form>
<div class="items methods" data-bind="foreach: shippingRates">
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
</div>

Now i need to subimit the form to this action url /rest//V1/shipping/estimate/:sku on change and get the result for a ko template.

Any help?

EDIT

Added the function getRates on js to get the shipping methods and it works, but how to reload the template (html update) when get e new response?

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.