Magento 2 add custom extesion html file in shipping method block
I am not sure if you can achieve that. Table with carrier rates is rendered based on rest request to rest/en/V1/guest-carts/estimate-shipping-methods
. You need to add extension attributes to ShippingMethodInterface
and add your extension attributes for the output of ShipmentEstimationInterface::estimateByExtendedAddress
and ShippingMethodManagementInterface::estimateByAddressId
. Then you'd need to override html file that is used for rendering (magento/module-checkout/view/frontend/web/template/shipping.html
either by changing elementTmpl
config in xml file or by creating the file in the correct path in your design folder) and change structure of the table by adding additional column.
[EDIT]
I am not sure if you still haven't found the answer but here is what need to be done to achieve this. Since I do not knwo what exactly do you want to display I will provide an example for adding information about delivery time for each carrier.
First we need to include data we want to display to the rest request for shipping rates. Magento can makes rest call to 2 endpoint depending on whether you are logged in and using saved address or not. One request is processed by Magento\Quote\Model\ShippingMethodManagement
class. Whichever method is actually used they all fetch rates from magento and create a list of Magento\Quote\Api\Data\ShippingMethodInterface
objects. Luckily for us this interface extends Magento\Framework\Api\ExtensibleDataInterface
so we can use magento2 extension attributes feature to hook in and add whatever we want.
In file Vendor/Namespace/etc/extension_attributes.xml
I add the following configuration
<?xml version="1.0"?>
<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\ShippingMethodInterface">
<attribute code="delivery_time" type="string" />
</extension_attributes>
</config>
This will inform magento that we want to allow delivery_date attribute to be in the api rest output. Without that definition whatever we add to the ShippingMethodInterface
object will be filtered out by magento and won't appear in the response.
Now we need to actually create some logic and insert data to the response. Since there are several methods which can be called from the frontend the best way to do this is to hook into Magento\Quote\Model\Cart\ShippingMethodConverter::modelToDataObject()
method. This method is called in each function that handles the request and actually it creates ShippingMethodInterface
object from data fetch from the carrier rate collector. We will define a plugin in Vendor/Namespace/etc/webapi_rest/di.xml
(this could also be defined in etc/di.xml
but moving the plugin definition to webapi_rest scope will run it only for rest calls.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Quote\Model\Cart\ShippingMethodConverter">
<plugin name="add_delivery_date_to_carrier" type="Vendor\Namespace\Plugin\Carrier\DeliveryDate" disabled="false" sortOrder="30"/>
</type>
</config>
So now we create the file
<?php
namespace Vendor\Namespace\Plugin\Carrier;
use Magento\Quote\Model\Cart\ShippingMethodConverter;
use Magento\Quote\Api\Data\ShippingMethodInterface;
use Magento\Quote\Api\Data\ShippingMethodExtensionFactory;
class DeliveryDate
{
/**
* @var ShippingMethodExtensionFactory
*/
protected $extensionFactory;
/**
* DeliveryDate constructor.
*
* @param ShippingMethodExtensionFactory $extensionFactory
*/
public function __construct(ShippingMethodExtensionFactory $extensionFactory)
{
$this->extensionFactory = $extensionFactory;
}
/**
* Add delivery date information to the carrier data object
*
* @param ShippingMethodConverter $subject
* @param ShippingMethodInterface $result
* @return ShippingMethodInterface
*/
public function afterModelToDataObject(ShippingMethodConverter $subject, ShippingMethodInterface $result)
{
$extensibleAttribute = ($result->getExtensionAttributes())
? $result->getExtensionAttributes()
: $this->extensionFactory->create();
$extensibleAttribute->setDeliveryTime(rand(1,5) . " days");
$result->setExtensionAttributes($extensibleAttribute);
return $result;
}
}
The plugin hooks after modelToDataObject method which returns ShippingMethodInterface
with single carrier data. From this object we try to get ExtensionAttributes object as other modules might have defined their own extension attributes and already added values. If not we create new object from factory class. This factory class will be generated by magento and can be found in var/generation
folder after doing setup di:compile
command. The logic here is simple, you can use any kind of custom logic you want. The most important part of this method however is to not forget returning the modified object. Otherwise we will see empty response as nothing will be passed from the call.
Ok. So now we have reponse like this:
[{
"carrier_code": "flatrate",
"method_code": "flatrate",
"carrier_title": "UPS/GLS",
"amount": 9.5,
"base_amount": 9.5,
"available": 1,
"extension_attributes": {
"delivery_time": "2 days"
},
"error_message": "",
"price_excl_tax": 9.5,
"price_incl_tax": 11.69,
}]
It's time to show it on the screen. And here is the biggest problem, we cannot easily (or at least I haven't found a way) hook data into the table. We need to replace template file that defines shipping methods table with our own. This can obviously cause conflicts with other modules as well be a reason why our changes won't be visible with custom checkout module (like one step checkout implementation). To make it work with magento default checkout we need to create file Vendor/Namespace/view/frontend/layout/checkout_index_index.xml
with the following content
<?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">
<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="config" xsi:type="array">
<item name="template" xsi:type="string">Vendor_Namespace/shipping</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</body>
</page>
What we actually do here is only changing the html file that will be used by the shipping section. Default file is defined in Magento_Checkout/view/frontend/web/js/view/shipping.js
file. The path we provided resolves to file Vendor/Namespace/view/frontend/web/template/shipping.html
. Here is the content of the file.
<!-- ko if: (!quoteIsVirtual) -->
<!-- ko foreach: getRegion('customer-email') -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko-->
<!--/ko-->
<!-- ko foreach: getRegion('address-list') -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko-->
<!-- ko foreach: getRegion('address-list-additional-addresses') -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko-->
<!-- Address form pop up -->
<!-- ko if: (!isFormInline) -->
<button type="button"
data-bind="click: showFormPopUp, visible: !isNewAddressAdded()"
class="action action-show-popup">
<span data-bind="i18n: 'New Address'"></span></button>
<div id="opc-new-shipping-address" data-bind="visible: isFormPopUpVisible()">
<!-- ko template: 'Magento_Checkout/shipping-address/form' --><!-- /ko -->
</div>
<!-- /ko -->
<!-- ko foreach: getRegion('before-form') -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko-->
<!-- Inline address form -->
<!-- ko if: (isFormInline) -->
<!-- ko template: 'Magento_Checkout/shipping-address/form' --><!-- /ko -->
<!-- /ko -->
</div>
</li>
<!--Shipping method template-->
<li id="opc-shipping_method"
class="checkout-shipping-method"
data-bind="fadeVisible: visible(), blockLoader: isLoading"
role="presentation">
<div class="checkout-shipping-method">
<div class="step-title" data-bind="i18n: 'Shipping Methods'" data-role="title"></div>
<!-- ko foreach: getRegion('before-shipping-method-form') -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!-- /ko -->
<div id="checkout-step-shipping_method"
class="step-content"
data-role="content"
role="tabpanel"
aria-hidden="false">
<!-- ko if: rates().length -->
<form class="form methods-shipping" id="co-shipping-method-form" data-bind="submit: setShippingInformation" novalidate="novalidate">
<div id="checkout-shipping-method-load">
<table class="table-checkout-shipping-method">
<thead>
<tr class="row">
<th class="col col-method" data-bind="i18n: 'Select Method'"></th>
<th class="col col-price" data-bind="i18n: 'Price'"></th>
<th class="col col-method" data-bind="i18n: 'Method Title'"></th>
<th class="col col-carrier" data-bind="i18n: 'Carrier Title'"></th>
<th class="col col-delivery" data-bind="i18n: 'Delivery Date'"></th>
</tr>
</thead>
<tbody>
<!--ko foreach: { data: rates(), as: 'method'}-->
<tr class="row" data-bind="click: $parent.selectShippingMethod">
<td class="col col-method">
<!-- ko ifnot: method.error_message -->
<!-- ko if: $parent.rates().length == 1 -->
<input class="radio"
type="radio"
data-bind="attr: {
checked: $parent.rates().length == 1,
'value' : method.carrier_code + '_' + method.method_code,
'id': 's_method_' + method.method_code,
'aria-labelledby': 'label_method_' + method.method_code + '_' + method.carrier_code + ' ' + 'label_carrier_' + method.method_code + '_' + method.carrier_code
}" />
<!-- /ko -->
<!--ko ifnot: ($parent.rates().length == 1)-->
<input type="radio"
data-bind="
value: method.carrier_code + '_' + method.method_code,
checked: $parent.isSelected,
attr: {
'id': 's_method_' + method.carrier_code + '_' + method.method_code,
'aria-labelledby': 'label_method_' + method.method_code + '_' + method.carrier_code + ' ' + 'label_carrier_' + method.method_code + '_' + method.carrier_code
},
click: $parent.selectShippingMethod"
class="radio"/>
<!--/ko-->
<!-- /ko -->
</td>
<td class="col col-price">
<!-- ko foreach: $parent.getRegion('price') -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!-- /ko -->
</td>
<td class="col col-method"
data-bind="text: method.method_title, attr: {'id': 'label_method_' + method.method_code + '_' + method.carrier_code}"></td>
<td class="col col-carrier"
data-bind="text: method.carrier_title, attr: {'id': 'label_carrier_' + method.method_code + '_' + method.carrier_code}"></td>
<td class="col col-delivery"
data-bind="text: method.extension_attributes.delivery_time"></td>
</tr>
<!-- ko if: method.error_message -->
<tr class="row row-error">
<td class="col col-error" colspan="4">
<div class="message error">
<div data-bind="text: method.error_message"></div>
</div>
<span class="no-display">
<input type="radio" data-bind="attr: {'value' : method.method_code, 'id': 's_method_' + method.method_code}"/>
</span>
</td>
</tr>
<!-- /ko -->
<!-- /ko -->
</tbody>
</table>
</div>
<div id="onepage-checkout-shipping-method-additional-load">
<!-- ko foreach: getRegion('shippingAdditional') -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!-- /ko -->
</div>
<!-- ko if: errorValidationMessage().length > 0 -->
<div class="message notice">
<span><!-- ko text: errorValidationMessage()--><!-- /ko --></span>
</div>
<!-- /ko -->
<div class="actions-toolbar" id="shipping-method-buttons-container">
<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>
<!-- /ko -->
<!-- ko ifnot: rates().length > 0 --><div class="no-quotes-block"><!-- ko i18n: 'Sorry, no quotes are available for this order at this time'--><!-- /ko --></div><!-- /ko -->
</div>
</div>
</li>
I used default file from Magento_Checkout module and added 2 elements: <th class="col col-delivery" data-bind="i18n: 'Delivery Date'"></th>
and <td class="col col-delivery"data-bind="text: method.extension_attributes.delivery_time"></td>
I hope this will be detailed enough to make it work with whatever data you want to display.