Akeneo technology iconHow to extend existing modules based on RequireJS?

How to extend existing modules based on RequireJS?

You are pleased with what the core module of Akeneo PIM offers but you would like to change one of the functionalities? As a Frontend Developer, you will frequently face this type of challenges because it is not always possible to cover every specific business need with the built-in component.

In this article we will show how to extend the RequireJS modules and import the original and extended file in the "requirejs.yml" files. The concept of inheritance is also important in this context as unchanged properties and methods of a parent module are inherited by our module.

Ready? Let’s dive in!


My blog post series provides some context and explanation of the principles used to build UI in the PIM. Check the other articles:


To override or to extend?

Modules are not all equal. In case of bigger RequireJS modules which have numerous methods and are used in plenty of places in the application, it wouldn't be the best practice to override them, which we discussed previously. A software update might also be a problem when overriding complex components.

If we want to customize or add a single method in a complex RequireJS component, we should extend it. The key benefit of this approach is that by extending a default module we override only the methods we want to modify, not the whole component.

The best practice while extending RequireJS module

Referring to the property inheritance, we will extend the existing RequireJS module and change the logic of the "createFields" method. We want to check if "name" is the current field and set a default value to it.

The "attributes" file is quite a complex module which is used in many places. It is responsible for loading information about fields and assigning proper context and value to them. Moreover, it is used to sort them out in the proper order. As an exercise, let’s modify the "createFields" method. You also need to check how this module is imported by default in the "requirejs.yml" files.

By default, it is added in the following way:

pim/form/common/attributes: pimui/js/form/common/attributes

The file we will extend can be found here:

In order to extend an existing module, it has to be imported using a different key and then added to the file in which we extend this module. Finally, the extended component is imported with the key of the old one.

The "attributes.js" file is added in RequireJS with a new key "base-attributes". Make sure that the given key is not used anywhere else in the project:

pim/form/common/base-attributes: pimui/js/form/common/attributes

The next step is to create a new "attributes" file which will extend the old one. We add the original "attributes.js" file to it using the key "pim/form/common/base-attributes".

In the given case, we copy only these methods that we want to extend from the original source file:

'use strict';

/**
* Override of the attributes module.
*
* Purpose of this override is to improve the blocking fields for simple products
* and sets of products including common attributes and variants
*
*/
define(
   [
       'jquery',
       'underscore',
       'oro/mediator',
       'pim/field-manager',
       'pim/attribute-manager',
       'pim/fetcher-registry',
       'pim/user-context',
       'pim/security-context',
       'pim/i18n',
       'pim/form/common/base-attributes'
   ],
   function (
       $,
       _,
       mediator,
       FieldManager,
       AttributeManager,
       FetcherRegistry,
       UserContext,
       SecurityContext,
       i18n,
       BaseAttributes
   ) {

       return BaseAttributes.extend({
           createFields: function (data, values) {
               return FetcherRegistry.getFetcher('attribute')
                   .fetchByIdentifiers(Object.keys(values))
                   .then(attributes => {
                       attributes.sort((a, b) => {
                           if (a.sortOrder  b.sortOrder) {
                               return -1;
                           }

                           if (a.sortOrder > b.sortOrder) {
                               return 1;
                           }

                           return a.meta.id  b.meta.id ? -1 : 1;
                       });

                       return $.when.apply(
                           $,
                           attributes.map(attribute => {
                               console.log(values);

                               return this.createAttributeField(data, attribute.code, values[attribute.code]);
                           })
                       );
                   })
                   .then(function () {
                       return _.values(arguments);
                   });
           }
       });
   }
);

After importing the original "attributes" file, it is available as "BaseAttributes". We have access to all the methods and attributes of the imported file, as "BaseAttributes.extend" was used.

The final step before editing the file itself is to add the file created by us to the "requirejs.yml" file. Remember to add the file with the same key that we used to add the extended module:

pim/form/common/base-attributes: pimui/js/form/common/attributes
pim/form/common/attributes: macopediaproduct/js/product/maco-attributes

In the first line, you can see the old "attributes" file which refers to "pim/form/common/base-attributes". Our module that extends the default "attributes" file is imported in the second line using the key "pim/form/common/attributes".

Now we can edit the module we created. At first we will use the "console.log" command to display the "values" variable in the "createFields" method which we want to edit. This command will help use verify if the file was added correctly and what data we have access to:

console.log(values);

If we did everything properly, we should see the following information for each field in the developer tools:

If we did everything properly, we should see the following information for each field in the developer tools

As you can see, we have access to numerous attributes, and one of them is "name".  Let’s now try to change the attribute "name" for the English version of the "name" field:

'use strict';

/**
* Override of the attributes module.
*
* Purpose of this override is to improve the blocking fields for simple products
* and sets of products including common attributes and variants
*
*/
define(
   [
       'jquery',
       'underscore',
       'oro/mediator',
       'pim/field-manager',
       'pim/attribute-manager',
       'pim/fetcher-registry',
       'pim/user-context',
       'pim/security-context',
       'pim/i18n',
       'pim/form/common/base-attributes'
   ],
   function (
       $,
       _,
       mediator,
       FieldManager,
       AttributeManager,
       FetcherRegistry,
       UserContext,
       SecurityContext,
       i18n,
       BaseAttributes
   ) {

       return BaseAttributes.extend({
           createFields: function (data, values) {
               return FetcherRegistry.getFetcher('attribute')
                   .fetchByIdentifiers(Object.keys(values))
                   .then(attributes => {
                       attributes.sort((a, b) => {
                           if (a.sortOrder  b.sortOrder) {
                               return -1;
                           }

                           if (a.sortOrder > b.sortOrder) {
                               return 1;
                           }

                           return a.meta.id  b.meta.id ? -1 : 1;
                       });

                       return $.when.apply(
                           $,
                           attributes.map(attribute => {
                               if (attribute.code === 'name') {
                                   values['name'][1].data = 'New description...';
                               }

                               return this.createAttributeField(data, attribute.code, values[attribute.code]);
                           })
                       );
                   })
                   .then(function () {
                       return _.values(arguments);
                   });
           }
       });
   }
);

We add a conditional function IF which would check if "name" is the current field, then we search for "name" in "values". There are a few objects in it for each language version. We want to override the English version, so we choose the second object and add a new value to the example "New description…".

Now you should be able to see the default value of the "name" field that we added:

Now you should be able to see the default value of the "name" field that we added

Did you find the steps listed in this article helpful? Stay up to date by following us on social media and make sure to connect with us on Twitter and LinkedIn! In the following chapter you will discover one more approach to module customization in Akeneo PIM. We will explain clearly how to add your own files and use them in the existing RequireJS modules.