Magento 2 – Execute Javascript After KnockoutJS Renders All Elements

javascriptknockoutjsmagento2

I am trying to run custom Javascript code after the page is loaded completely with all the form elements/fields.

I am trying this on Magento2 Admin while adding new product.

For example:

I am trying to disable Quantity field in New Product add page in
Magento2 Admin.

The page content is rendered through KnockoutJS. So, the following jQuery on load are not working:

jQuery(window).bind("load", function() { 
    // your code
});

// OR

jQuery(window).on('load', function() {
    // your code
});

How to run Javascript code in Magento 2 after KnockoutJS has completed rendering all the DOM elements?

Best Answer

Because Magento use async rendering it not possible to make what you ask.

But you can work with js UI component to disable field

require(['uiRegistry'], function (uiRegistry) {
    uiRegistry.get("product_form.product_form.product-details.quantity_and_stock_status_qty.qty", function (element) {
        element.disable()
    })
})

This answer is correct, however we're lacking information regarding how we know to use 'product_form.product_form.product-details.quanityt_and_stock_status_qty.qty' as the get() parameter.

.get is looking for its first parameter to be a component. So... how do you find the component, and is that language clear? No.

As an example, I wanted to add some custom javascript functionality to the customer information page within the Magento 2 admin. Specifically under, editing a user, 'Account Information' tab.

There are two ways to approach this from what I can tell...

Edit the datasource, to change values. Manipulate the field, such as visibility.

Edit the datasource:

Lets roll. Some dots are missing here, but we're on a page handled by the magento customer module, its an edit page... so we look here... /vendor/module-customer/view/adminhtml/layout/customer_index_edit.xml. Within here the content area is defined to contain 'customer_form'.

<referenceContainer name="content">
    <uiComponent name="customer_form"/>
</referenceContainer>

This means we're looking for a 'customer_form.xml' file, in this case it is found under /vendor/module-customer/view/base/ui_component/customer_form.xml. Within here we find the definition for a couple of the forms within the tabs and their fields. The datasource we're after is defined near the top.

<argument name="data" xsi:type="array">
    <item name="js_config" xsi:type="array">
        <item name="provider" xsi:type="string">customer_form.customer_form_data_source</item>
    </item>
    <item name="label" xsi:type="string" translate="true">Customer Information</item>
    <item name="reverseMetadataMerge" xsi:type="boolean">true</item>
</argument>

Note the 'provider' of 'customer_form.customer_form_data_source'. This is where all of our field data will exist within the .get call.

Our .get() would now become:

uiRegistry.get('customer_form.customer_form_data_source', function(element) {
    console.log(element.data);
});

Now, the question becomes... what can we do with this- and all you can do is adjust the data. You can turn fields true/false, manipulate values, so on. Useful.

Manipulate the field:

What if we want to hide the field and only show it under x condition... we can't do that with the datasource.

Now... this is going to be long and seem convoluted. Its because I'm teaching how to find your data... as I don't know how to find this any other way- and its going to be different per module. Teach a man to fish.

First I output my 'customer_form.customer_form_data_source' within our .get(). This will provide us a 'params' object key, within that is the definition of an 'activeArea' on the object, which is 'customer_form.areas.customer_edit_tab_view_content'.

Ok... so there are 'areas'. Areas, are the tabs on the left- each one is an area. Open up the '_elems' within your console, you'll notice 9 objects at the top. Open the second one and you'll find a label of 'Account Information'. This is the section I'm after. Then observe the 'name' value of 'customer_form.areas.customer'... ok lets try that as our get() param.

uiRegistry.get('customer_form.areas.customer', function(element) {
    console.log(element);
});

This is our fieldset wrapper, check its name value within the console under '_elems'->0->'name'. 'customer_form.areas.customer.customer'... we're drilling down. We're actually already there, check the 'name' value on the field you want to edit. You'll notice its name is 'customer[field_name]'. Making our path for get() is 'customer_form.areas.customer.customer.['field_name]'.

uiRegistry.get('customer_form.areas.customer.customer.[field_name]', function(element) {
    element.visible(false);
});

Examples: [form_name].[tab_areas].[area_name].[fieldset_name].[field_name] customer_form.areas.customer.customer.field_name