3

I'm modifying all Magento 2 to accept login with mobile phone instead of email. Register, login, account info, backend, login at checkout, everything is going well.

But when I use my own checkout_index_index.xml instead of the core one, the object which stores the list of renders of each payment method (rendererList) is not filled in.

In the moment of the checkout, using the original checkout_index_index.xml I got this when I debug list.js:

rendered list in the beginning >> 
[Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object] list.js:30 

Using my checkout_index_index.xml:

rendered list in the beginning >> 
[] list.js:30 

The object with the payment methods in both situations:

[Object, Object, Object, Object]
0: Object
method: "paypal_express"
title: "PayPal Express Checkout"
__proto__: Object
1: Object
method: "purchaseorder"
title: "Purchase Order"
__proto__: Object
2: Object
method: "checkmo"
title: "Check / Money order"
__proto__: Object
3: Object
method: "free"
title: "No Payment Information Required"
__proto__: Object
length: 4
__proto__: Array[0]

So, the rendererList used in list.js to render each payment method for my custom module is empty. I spend a whole day trying to find where does this object is filled in without success.

I must say I'm editing checkout_index_index.xml to use my own js files on checkout steps like this:

<item name="component" xsi:type="string">Tmo_Checkout/js/view/shipping</item>
     <item name="provider" xsi:type="string">checkoutProvider</item>
     <item name="sortOrder" xsi:type="string">1</item>
     <item name="children" xsi:type="array">
           <item name="customer-email" xsi:type="array">
                <item name="component" xsi:type="string">Tmo_Checkout/js/view/form/element/username</item>
                <item name="displayArea" xsi:type="string">customer-email</item>
                <item name="tooltip" xsi:type="array">
                      <item name="description" xsi:type="string" translate="true">We'll send your order confirmation here.</item>
                </item>
                <item name="children" xsi:type="array">
                      <item name="before-login-form" xsi:type="array">
                      <item name="component" xsi:type="string">uiComponent</item>
                      <item name="displayArea" xsi:type="string">before-login-form</item>
                      <item name="children" xsi:type="array">
                            <!-- before login form fields -->
                      </item>
               </item>
               <item name="additional-login-form-fields" xsi:type="array">
                   <item name="component" xsi:type="string">uiComponent</item>
                   <item name="displayArea" xsi:type="string">additional-login-form-fields</item>
                   <item name="children" xsi:type="array">
                      <!-- additional login form fields -->
               </item>
          </item>
     </item>
</item>

Finally, the questions are: how do I fill that rendererList?? or where Magento does it?? or how can I specify custom js files for checkout without touching checkout_index_index.xml?? Thanks in advance!

5

The rendererList is observable JavaScript array provided by the Magento_Checkout/js/model/payment/renderer-list component.

In order to use rendererList in custom payment module the following should be done:

  1. Create in your module view/frontend/layout/checkout_index_index.xml file and declare new payment renderer component (adding as an image to give an idea):

enter image description here

  1. Create the Custom_Payment/js/view/payment/custom_payment.js component and add rendererList component as a dependency. It will allow you to add custom payment renderer into rendererList array:
define(
    [
        'uiComponent',
        'Magento_Checkout/js/model/payment/renderer-list'
    ],
    function (
        Component,
        rendererList
    ) {
        'use strict';
        rendererList.push(
            {
                type: 'custom_payment',
                component: 'Custom_Payment/js/view/payment/method-renderer/custom-payment-renderer'
            }
        );
        /** Add view logic here if needed */
        return Component.extend({});
    }
);
  1. Create Custom_Payment/js/view/payment/method-renderer/custom-payment-renderer component and assign template to render custom payment:
define(
    [
        'jquery',
        'Magento_Payment/js/view/payment/iframe',
        'Magento_Checkout/js/action/set-payment-information'
    ],
    function ($, Component, setPaymentInformationAction) {
        'use strict';

        return Component.extend({
            defaults: {
                template: 'Custom_Payment/payment/custom-payment'
            },
            ...
        });
    }
);
  1. Finally, create Custom_Payment/view/frontend/web/template/payment/custom-payment.html template to have custom payment form.

All Core Payment modules modifies rendererList in the way described above.

I hope answer gives you an idea how rendererList is filled with payment methods.

It is recommended to add custom js files via checkout_index_index.xml. This has to be done very carefully, respecting nested levels of jsLayout component.

Let me know if you still have questions.

  • I'm not adding a new custom payment method, what I need is to add my custom JS files to the checkout_index_index.xml thats the only thing I need. But when I copy that file to my module folder in order to edit it, then what I get is a completely empty rendererList. I saw before those JS files filling in the rendererList... But, do I need to have a copy of all of them in my folders to fill in that list of renders?? – hiperboreo Jan 18 '16 at 0:53
  • @hiperboreo would you mind to show your version of checkout_index_index.xml file to understand the issue? – Max Pronko Jan 18 '16 at 8:52
  • I edited my question, I added a piece of checkout_index_index.xml where I'm using custom JS files instead of core JS files. Right now I'm thinking in intercept the layout creation on class LayoutProcessor, I'll try it tomorrow. Thanks. – hiperboreo Jan 18 '16 at 10:03
3

After a while, instead of intercept the layout creation to rewrite the section where I wanted use my JS files instead of the core original ones, I delete that code and replace it with a require-js file, like this:

Path: MyCompany/Checkout/view/frontend/requirejs-config.js

Content:

var config = {
map: {
    '*': {
        'Magento_Checkout/js/model/customer-email-validator':'MyCompany_Checkout/js/model/customer-email-validator',
        'Magento_Checkout/js/model/step-navigator':'MyCompany_Checkout/js/model/step-navigator',
        'Magento_Checkout/js/view/payment/email-validator':'MyCompany_Checkout/js/view/payment/email-validator',
        'Magento_Checkout/js/view/form/element/email':'MyCompany_Checkout/js/view/form/element/username',
        'Magento_Checkout/js/view/shipping':'MyCompany_Checkout/js/view/shipping',
        'Magento_Checkout/js/view/authentication':'MyCompany_Checkout/js/view/authentication',
        'Magento_Checkout/js/checkout-data':'MyCompany_Checkout/js/checkout-data',
        'Magento_Checkout/js/model/shipping-save-processor/default':'MyCompany_Checkout/js/model/shipping-save-processor/default'
    }
}
};

It was so much simpler than my first answer and works exactly the same but with less effort :)

0

I already found how to solve my problem, it's interesting, I explain it:

1.- I already had a custom Checkout module, I created the next preference on my di.xml:

<preference for="Magento\Checkout\Block\Checkout\LayoutProcessor"
           type="Tmo\Checkout\Block\Checkout\LayoutProcessorPreference" />

That class receives the content of checkout_index_index.xml as an array, so we can access it in the same way.

2.- I modified the content of process function, adding the following:

if(isset($jsLayout['components']['checkout']['children']['steps']
         ['children']['shipping-step']['children']['shippingAddress']
         ['children']['customer-email']['component']))
{
  $jsLayout['components']['checkout']['children']['steps']['children']
           ['shipping-step']['children']['shippingAddress']['children']
           ['customer-email']['component'] = 'Tmo_Checkout/js/view/form/element/username';
}

if(isset($jsLayout['components']['checkout']['children']['authentication']['component']))
{
      $jsLayout['components']['checkout']['children']['authentication']['component'] = 'Tmo_Checkout/js/view/authentication';
}

And so on... What I'm doing is following the nested structure of checkout_index_index.xml until I reach the tag I need to change. To know this structure I printed the entire $jsLayout to the debug log and looked for my tag.

I have 5 blocks like these, each one checks if the content exists on that specific part of the array and then replace the original string, which is a JS path/filename, with a string containing my custom JS path/filename.

The above code is working as expected :)

This class also adds, or more specifically 'merge', into that layout more stuff like the address fields, shipment methods, renders for payment methods, etc...

I choose to use a preference instead of a plugin because I'm also using protected attributes on other part of this function.

  • Hello.... I need to use also my own LayoutProcessor instead of Magento\Checkout\Block\Checkout\LayoutProcessor but my own is not being called. What else did you do? – jstuardo Mar 12 '16 at 0:09
  • It works if you create a preference for that Magento\Checkout\Block\Checkout\LayoutProcessor – hiperboreo Mar 14 '16 at 1:46
0

Debug entry point is \Magento\Vault\Plugin\PaymentVaultConfigurationProcess::beforeProcess.

You can check if your method is in the active list and why it is deactivated.

Your Answer

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

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