Hello @SannNL
you need to put code into
module-checkout\view\frontend\web\js\view\payment\default.js
If work then marks as solution.
Ahh that explains a lot haha.
But i'm a bit confused now. I still get the same error and now i have the following files:
Vendor/Module/view/frontend/web/js/view/payment/default.js
define([ 'ko', 'jquery', 'uiComponent', 'Magento_Checkout/js/action/place-order', 'Magento_Checkout/js/action/select-payment-method', 'Magento_Checkout/js/model/quote', 'Magento_Customer/js/model/customer', 'Magento_Checkout/js/model/payment-service', 'Magento_Checkout/js/checkout-data', 'Magento_Checkout/js/model/checkout-data-resolver', 'uiRegistry', 'Magento_Checkout/js/model/payment/additional-validators', 'Magento_Ui/js/model/messages', 'uiLayout', 'Magento_Checkout/js/action/redirect-on-success', 'Magento_Checkout/js/model/step-navigator' ], function ( ko, $, Component, placeOrderAction, selectPaymentMethodAction, quote, customer, paymentService, checkoutData, checkoutDataResolver, registry, additionalValidators, Messages, layout, redirectOnSuccessAction, stepNavigator ) { 'use strict'; return Component.extend({ redirectAfterPlaceOrder: true, isPlaceOrderActionAllowed: ko.observable(quote.billingAddress() != null), /** * After place order callback */ afterPlaceOrder: function () { // Override this function and put after place order logic here }, /** * Initialize view. * * @return {exports} */ initialize: function () { var billingAddressCode, billingAddressData, defaultAddressData; this._super().initChildren(); quote.billingAddress.subscribe(function (address) { this.isPlaceOrderActionAllowed(address !== null); }, this); checkoutDataResolver.resolveBillingAddress(); billingAddressCode = 'billingAddress' + this.getCode(); registry.async('checkoutProvider')(function (checkoutProvider) { defaultAddressData = checkoutProvider.get(billingAddressCode); if (defaultAddressData === undefined) { // Skip if payment does not have a billing address form return; } billingAddressData = checkoutData.getBillingAddressFromData(); if (billingAddressData) { checkoutProvider.set( billingAddressCode, $.extend(true, {}, defaultAddressData, billingAddressData) ); } checkoutProvider.on(billingAddressCode, function (providerBillingAddressData) { checkoutData.setBillingAddressFromData(providerBillingAddressData); }, billingAddressCode); }); return this; }, /** * Initialize child elements * * @returns {Component} Chainable. */ initChildren: function () { this.messageContainer = new Messages(); this.createMessagesComponent(); return this; }, /** * Create child message renderer component * * @returns {Component} Chainable. */ createMessagesComponent: function () { var messagesComponent = { parent: this.name, name: this.name + '.messages', displayArea: 'messages', component: 'Magento_Ui/js/view/messages', config: { messageContainer: this.messageContainer } }; layout([messagesComponent]); return this; }, /** * Place order. */ placeOrder: function (data, event) { var self = this; if (event) { event.preventDefault(); } if (this.validate() && additionalValidators.validate()) { this.isPlaceOrderActionAllowed(false); this.getPlaceOrderDeferredObject() .fail( function () { self.isPlaceOrderActionAllowed(true); } ).done( function () { self.afterPlaceOrder(); if (self.redirectAfterPlaceOrder) { redirectOnSuccessAction.execute(); } } ); return true; } return false; }, /** * @return {*} */ getPlaceOrderDeferredObject: function () { return $.when( placeOrderAction(this.getData(), this.messageContainer) ); }, /** * @return {Boolean} */ selectPaymentMethod: function () { selectPaymentMethodAction(this.getData()); checkoutData.setSelectedPaymentMethod(this.item.method); return true; }, isChecked: ko.computed(function () { return quote.paymentMethod() ? quote.paymentMethod().method : null; }), isRadioButtonVisible: ko.computed(function () { return paymentService.getAvailablePaymentMethods().length !== 1; }), /** * Get payment method data */ getData: function () { return { 'method': this.item.method, 'po_number': null, 'additional_data': null }; }, /** * Get payment method type. */ getTitle: function () { return this.item.title; }, /** * Get payment method code. */ getCode: function () { return this.item.method; }, /** * @return {Boolean} */ validate: function () { return true; }, /** * @return {String} */ getBillingAddressFormName: function () { return 'billing-address-form-' + this.item.method; }, /** * Dispose billing address subscriptions */ disposeSubscriptions: function () { // dispose all active subscriptions var billingAddressCode = 'billingAddress' + this.getCode(); registry.async('checkoutProvider')(function (checkoutProvider) { checkoutProvider.off(billingAddressCode); }); }, goToPrevStep:function(){ stepNavigator.next(); } }); });
Vendor/Module/view/frontend/web/template/payment.html
<li id="payment" role="presentation" class="checkout-payment-method" data-bind="fadeVisible: isVisible"> <div id="checkout-step-payment" class="step-content" data-role="content" role="tabpanel" aria-hidden="false"> <!-- ko if: (quoteIsVirtual) --> <!-- ko foreach: getRegion('customer-email') --> <!-- ko template: getTemplate() --><!-- /ko --> <!--/ko--> <!--/ko--> <form id="co-payment-form" class="form payments" novalidate="novalidate"> <input data-bind='attr: {value: getFormKey()}' type="hidden" name="form_key"/> <fieldset class="fieldset"> <legend class="legend"> <span data-bind="i18n: 'Payment Information'"></span> </legend><br /> <!-- ko foreach: getRegion('beforeMethods') --> <!-- ko template: getTemplate() --><!-- /ko --> <!-- /ko --> <div id="checkout-payment-method-load" class="opc-payment" data-bind="visible: isPaymentMethodsAvailable"> <!-- ko foreach: getRegion('payment-methods-list') --> <!-- ko template: getTemplate() --><!-- /ko --> <!-- /ko --> </div> <div class="no-quotes-block" data-bind="visible: isPaymentMethodsAvailable() == false"> <!-- ko i18n: 'No Payment method available.'--><!-- /ko --> </div> <!-- ko foreach: getRegion('afterMethods') --> <!-- ko template: getTemplate() --><!-- /ko --> <!-- /ko --> <div class="back"> <a data-bind="click: goToPrevStep($parents[1])" aria-describedby="checkout-back" class="action back"> <span data-bind="i18n: '< Back'">< Back</span> </a> </div> </fieldset> </form> </div> </li>
Vendor/Module/view/frontend/web/template/payment/default.html (this doesn't really link to anything?)
<!-- /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ --> <div class="actions-toolbar" id="review-buttons-container"> <div class="primary"> <button data-role="review-save" type="submit" data-bind="click: placeOrder($parents[1]), attr: {title: $t('Place Order')}" class="button action primary checkout"><span data-bind="i18n: 'Place Order'"></span></button> </div> <div class="secondary"> <span id="checkout-review-edit-label" data-bind="i18n: 'Forgot an Item?'"></span> <a data-bind="attr: {href: $parents[1].cartUrl}" aria-describedby="checkout-review-edit-label" class="action edit"> <span data-bind="i18n: 'Edit Your Cart'"></span> </a> </div> <div class="back"> <a data-bind="click: goToPrevStep(click: goToPrevStep($parents[1]))" aria-describedby="checkout-back" class="action back"> <span data-bind="i18n: 'Back'">Back</span> </a> </div> </div>
Vendor/Module/view/frontend/requirejs-config.js
var config = { config: { mixins: { 'Magento_Checkout/js/view/shipping': { 'BB_Checkout/js/mixin/shipping-mixin': true } } }, 'map': { '*': { 'Wezz_Postcode/js/view/postcode': 'BB_Checkout/js/view/postcode-overwrite', 'Magento_Checkout/template/shipping-information/address-renderer/default': 'BB_Checkout/template/shipping-information/address-renderer/default-overwrite', 'ui/template/form/element/input': 'BB_Checkout/templates/form/element/input-overwrite', 'ui/template/form/element/email': 'BB_Checkout/templates/form/element/email-overwrite', 'ui/template/form/element/password': 'BB_Checkout/templates/form/element/password-overwrite', 'ui/template/form/field': 'BB_Checkout/templates/form/field-overwrite', 'Magento_Checkout/template/payment': 'BB_Checkout/template/payment', 'Magento_Checkout/js/view/payment/default.js': 'BB_Checkout/js/view/payment/default.js' } } };
Error: Uncaught ReferenceError: Unable to process binding "click: function (){return goToPrevStep($parents[1]) }" Message: goToPrevStep is not defined.
Hello @SannNL
Sorry for the late reply
Here is final code
<div class="back"> <a data-bind="click: goToPrevStep" aria-describedby="checkout-back" class="action back"> <span data-bind="i18n: 'Back'"></span> </a> </div>
you need to add above code into that payment method phtml where you added form and place order button.
Also payment for method you created method-renderer/payment.js
where payment.js may be your payment js file name
in that js file
define( [ 'Magento_Checkout/js/view/payment/default', 'jquery', 'Magento_Checkout/js/model/quote', 'Magento_Customer/js/customer-data', 'Magento_Catalog/js/price-utils', 'mage/validation', 'mage/translate', 'Magento_Checkout/js/model/step-navigator' ],function (Component, $, quote, customerData, priceUtils,validation,translate,stepnavigator) { return Component.extend({ .................. goToPrevStep :function() { stepnavigator.navigateTo('shipping'); } }) };
If it will work then mark as solution.
Hello, sorry I had the day of because of kingsdag. I tried your example but i'm unsure on what js file exactly i need to change or extend or anything. Because I keep getting the error that the function is undefined so I definitely have the wrong Js file.
Also in Checkout > view > frontend > web > template > payment.html the data-bind i18n: 'Back' doesn't get shown.
Hello @SannNL
can I know for which payment are you doing?
I'm trying to add it in the button of the payment page, not in one method. Like this:
Hello @SannNL
If you want to do for all payment method then you need to edit that related phtml file and js file
e.g
https://github.com/magento/magento2/blob/2.2-develop/app/code/Magento/OfflinePayments/view/frontend/... - here you need to add your button code for back.
here you need to your back js code, same for all other payment method.
Hope it will help you.
If it will help you then mark as a solution or give us kudos.
I ended up extending the xml with a custom component and adding JS to that.
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"> <body> <referenceContainer name="content"> <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="afterMethods" xsi:type="array"> <item name="children" xsi:type="array"> <item name="back-button" xsi:type="array"> <item name="sortOrder" xsi:type="string">35</item> <item name="component" xsi:type="string">uiComponent</item> <item name="config" xsi:type="array"> <item name="template" xsi:type="string">BB_Checkout/back-button</item> <item name="component" xsi:type="string">BB_Checkout/js/view/back-button</item> </item> </item> </item> </item> </item> </item> </item> </item> </item> </item> </item> </item> </item> </argument> </arguments> </referenceBlock> </referenceContainer> </body> </page>
back-button.js
define([ 'uiComponent', 'Magento_Checkout/js/model/step-navigator' ], function (Component, stepNavigator) { return Component.extend({ goToPrevStep: function () { stepNavigator.navigateTo('shipping'); } }) });
back-button.html
<div class="back"> <a data-bind="click: goToPrevStep()" aria-describedby="checkout-back" class="action back"> <span data-bind="i18n: '< Back'"></span> </a> </div>
Thanks for the help