7

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?

2

5 Answers 5

16

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

2
  • Can you please explain how to get identifier for a field in ui component? Commented Jan 18, 2018 at 8:15
  • You can find registry-names of any KnockoutJS elements easily using the following Google Chrome-extension: Knockoutjs context debugger. Just click on the element you want to know the name of and find the 'name'-element inside the output. Commented Jun 21, 2018 at 12:37
8

There is a plugin built into Require JS to handle page load, this is the preferred method as any other modules that depend on yours will also be aware of the page load requirement. If you use the jQuery method mentioned by Rajeev the other modules will not be aware of the page load requirement.

The plugin is called domReady! and can be used by adding it as a dependency like so:

require(["jquery", "domReady!"], function($){
    // Your code here
});

This will wait for page load but doesn't necessarily mean it loads after all other KO modules as they may also be waiting for page load. For that reason I think KAndy's method is the most reliable, but this answer may help others.

1
3

doing this works for me, it waits until ko has put the form elements in

require([
'jquery',
'Magento_Ui/js/lib/view/utils/dom-observer',
], function ($,$do) {
    $(document).ready(function(){
        $do.get('.class-name', function(elem){
            $(elem).addClass('test-class');
            //your code
        });
    });
});
2
  • 1
    This works as expected and is actually quite simple. +1
    – Akif
    Commented May 31, 2022 at 9:47
  • 1
    @John, many thanks. This helped, because in my case the issue was to execute script after customer open Account Information tab. So, I've just added the block to customer_index_edit.xml and your script in phtml file and it works as I need. Commented Dec 16, 2022 at 15:44
2

The document ready or DOM loaded events won't be fired on completion of UI form loading.

Here, either you shall have to write a function in javascript which should be called at some interval and as soon as your target element appears in DOM, process your business logic and clear the interval.

Another option could be - to execute your JavaScript business logic after completion of AJAX request which can be achieved using following code block.

$(document).ajaxComplete(function() {
// enter your code here
});
0

You can try something like this.

<script type="text/javascript">
require(["jquery"], function($){
    $(function() {

        //do your stuff here

    });
});
</script>

This will make sure, your code will get executed only after dom is fully loaded. If you are familiar with jquery, you will be probably know what $(function() {}); does. It makes sure code comes inside that anonymous function will get executed only after DOM is completely loaded.

1
  • 4
    Thanks for the answer but it didn't work. The jQuery code runs before KnockoutJS renders DOM elements. Commented Feb 12, 2017 at 16:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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