OWL in Odoo 14 - How to extend and patch existing OWL Components.
Tags: odoo, owl, javascript
Published: 2021-08-15 14:34:26

---
title: OWL in Odoo 14 - How to extend and patch existing OWL Components.
published: true
date: 2021-08-08 10:42:17 UTC
tags: Odoo,OWL,JavaScript
canonical_url: https://codingdodo.com/owl-in-odoo-14-extend-and-patch-existing-owl-components/
---

 ![OWL in Odoo 14 - How to extend and patch existing OWL Components.](https://codingdodo.com/content/images/2021/08/post-cover-odoo-14-owl-extend-override-2.png)

In this article, we will see how to extend, monkey-patch and, modify existing OWL Components in Odoo 14. There is a lot of confusion about that, and the existing way of overriding Odoo Widget doesn't work in that case.

We will focus on OWL Components inside Odoo 14, the process will probably be different in Odoo 15 since the WebClient has been entirely rewritten in OWL.

{% twitter 1406156438012149771 %}


Finally, this article assumes you have a good understanding of OWL already, if this is not the case check out [this article series where we create the Realworld App with OWL](https://codingdodo.com/realworld-app-with-owl-odoo-web-library-part-1/) and go other most of the functionalities of the Framework.

All OWL-related content [is available here](https://codingdodo.com/tag/owl/).

## Introduction

First and foremost OWL Components are **ES6 Classes** , if you are not familiar with ES6 classes, you can visit this [Google presentation article](https://googlechrome.github.io/samples/classes-es6/). I would also refer you to the amazing book [You Don't Know JS: ES6 and Beyond](https://www.amazon.com/gp/product/1491904240/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=codingdodo-20&creative=9325&linkCode=as2&creativeASIN=1491904240&linkId=71fbf7538468b0652bdc59402caa7724).

ES6 Classes are basically syntactical sugar over the existing prototype-based inheritance in JavaScript. At the most basic level, an ES6 Class is a constructor that conforms to prototype-based inheritance. ES6 classes still have `Object.prototype`!

To go deeper on this subject I would recommend this article about [the difference between these ES6 classes and prototypes](https://www.toptal.com/javascript/es6-class-chaos-keeps-js-developer-up). This is a very confusing subject, but this quote from the article is very relevant


> _The most important difference between class- and prototype-based inheritance is that a class defines a type which can be instantiated at runtime, whereas a prototype is itself an object instance._


Anyway, to work with Odoo 14 existing OWL Components, you still have to know some general concepts. We will keep it to a bare minimum, so let's begin with what an ES6 Class look like!

```javascript
class Component {
  constructor(name) {
    this.name = name;
  }

  render() {
    console.log(`${this.name} renders itself.`);
  }
    
  // Getter/setter methods are supported in classes,
  // similar to their ES5 equivalents
  get uniqueId() {
    return `${this.name}-test`;
  }
}
```

You can inherit classes with the keyword **`extends`** and `super` to call parent function.

```javascript
class MyBetterComponent extends Component {
  constructor(name) {
    super(name); // call the super class constructor and pass in the name parameter
  }

  render() {
    console.log(`${this.name} with id ${this.uniqueId} render itslef better.`);
  }
}

let comp = new MyBetterComponent('MyBetterComponent');
comp.render(); // MyBetterComponent with id MyBetterComponent-test renders itself better.
```

_This is the standard ES6 `super` keyword, don't confuse it with the Odoo `_super` function built inside the Framework._

Most of the patching, extending, overriding of OWL Components in Odoo will make use of this basic knowledge, so let's dive in. Everything will become clearer with examples.

## Odoo OWL utils patch, and patchMixin functions.

Extending Odoo OWL components is done via a `patch` function that comes in 2 flavors. Either the Component itself exposes a `patch` function because it is wrapped around the `patchMixin`. Or you have to use the `patch` function directly (in the web.utils package) to apply a patch to an OWL Component.

### With the patchMixin, the Component exposes a "patch" function.

Inside odoo/addons/web/static/src/js/core/patch\_mixin.js we have this `patchMixin`function:

```javascript
function patchMixin(OriginalClass) {
    let unpatchList = [];
    class PatchableClass extends OriginalClass {}

    PatchableClass.patch = function (name, patch) {
        if (unpatchList.find(x => x.name === name)) {
            throw new Error(`Class ${OriginalClass.name} already has a patch ${name}`);
        }
        if (!Object.prototype.hasOwnProperty.call(this, 'patch')) {
            throw new Error(`Class ${this.name} is not patchable`);
        }
        const SubClass = patch(Object.getPrototypeOf(this));
        unpatchList.push({
            name: name,
            elem: this,
            prototype: this.prototype,
            origProto: Object.getPrototypeOf(this),
            origPrototype: Object.getPrototypeOf(this.prototype),
            patch: patch,
        });
        Object.setPrototypeOf(this, SubClass);
        Object.setPrototypeOf(this.prototype, SubClass.prototype);
    };

    PatchableClass.unpatch = function (name) {
        if (!unpatchList.find(x => x.name === name)) {
            throw new Error(`Class ${OriginalClass.name} does not have any patch ${name}`);
        }
        const toUnpatch = unpatchList.reverse();
        unpatchList = [];
        for (let unpatch of toUnpatch) {
            Object.setPrototypeOf(unpatch.elem, unpatch.origProto);
            Object.setPrototypeOf(unpatch.prototype, unpatch.origPrototype);
        }
        for (let u of toUnpatch.reverse()) {
            if (u.name !== name) {
                PatchableClass.patch(u.name, u.patch);
            }
        }
    };
    return PatchableClass;
}
```


A Component using this `patchMixin` is returned wrapped around the function, for example inside odoo/addons/mail/static/src/components/messaging\_menu/messaging\_menu.js the MessagingMenu is returned like that:

```javascript
// ...
const patchMixin = require('web.patchMixin');

const { Component } = owl;

class MessagingMenu extends Component {
// ...
// content of the file
// ...
}
return patchMixin(MessagingMenu);
```

Be careful, there are actually **not that many Components that are returned with the `patchMixin`** , you should always check first if that is the case. We will call these kinds of components "Patchable Components".

### Import "web.utils", patch function for a "non-patchable" Component as a last resort.

When the Component doesn't use the `patchMixin` you will not be able to **extend** the ES6 class properly but with the **`patch`** function you will be able to override the _regular functions_ of the Component.


> The web.utils `patch`function is kind of limited, and will work on the **"** regular functions **"** of the component. Constructor, getters, setters won't be inherited with technique as we will see in examples later.


This is the patch function content:

```javascript
/**
 * Patch a class and return a function that remove the patch
 * when called.
 *
 * This function is the last resort solution for monkey-patching an
 * ES6 Class, for people that do not control the code defining the Class
 * to patch (e.g. partners), and when that Class isn't patchable already
 * (i.e. when it doesn't have a 'patch' function, defined by the 'web.patchMixin').
 *
 * @param {Class} C Class to patch
 * @param {string} patchName
 * @param {Object} patch
 * @returns {Function}
 */
patch: function (C, patchName, patch) {
    let metadata = patchMap.get(C.prototype);
    if (!metadata) {
        metadata = {
            origMethods: {},
            patches: {},
            current: []
        };
        patchMap.set(C.prototype, metadata);
    }
    const proto = C.prototype;
    if (metadata.patches[patchName]) {
        throw new Error(`Patch [${patchName}] already exists`);
    }
    metadata.patches[patchName] = patch;
    applyPatch(proto, patch);
    metadata.current.push(patchName);

    function applyPatch(proto, patch) {
        Object.keys(patch).forEach(function (methodName) {
            const method = patch[methodName];
            if (typeof method === "function") {
                const original = proto[methodName];
                if (!(methodName in metadata.origMethods)) {
                    metadata.origMethods[methodName] = original;
                }
                proto[methodName] = function (...args) {
                    const previousSuper = this._super;
                    this._super = original;
                    const res = method.call(this, ...args);
                    this._super = previousSuper;
                    return res;
                };
            }
        });
    }

    return utils.unpatch.bind(null, C, patchName);
},
```

As you may already see, the content of this function is problematic, it directly touches the **prototype** of the Object and do some checks on the `typeof == "function"` that can be misleading...


> A Component returned from its module wrapped around the `patchMixin` has a `patch` function that you can use, if not your last resort is the `web.utils` general `patch` function.


In conclusion, this is what we have to work with. Now we will go through real world examples on how to apply this knowledge and see some specific cases.

* * *

## Patchable Component (returned with "patchMixin"): Extend, monkey-patch, override.

### Basic syntax

The basic syntax of extending a patchable component is:

```javascript
PatchableComponent.patch("name_of_the_patch", (T) => {
    class NewlyPatchedComponent extends T {
        //... go wild 
    }
    return NewlyPatchedComponent
})
```

With this patch, you really play with ES6 classes syntax. **Your extended Component is also an ES6 class** so you can touch the constructor, getters, setters, properties, and other functions.

### Example: the ControlPanel Component.

In this example, we will extend the ControlPanel Component. This component is returned with the `patchMixin` function, original file:

```javascript
// at the end of file...
ControlPanel.template = 'web.ControlPanel';

return patchMixin(ControlPanel);

```

### Describing the functionality.

The goal of our module is to be very obnoxious, we will be to display a message, under the ControlPanel (everywhere) that will call an API and show a random _inspiring_ quote from some famous people.

_Please don't use this code in a real project, everybody will hate you secretly._

To make our `fetch` request to our quotes API we will use the `willUpdateProps` hook so every time the user navigates on his WebClient it will fetch a new quote!


> _If you are not familiar with OWL hooks and particularly `willUpdateProps`, there is_ [_series about OWL from scratch here_](https://codingdodo.com/realworld-app-with-owl-odoo-web-library-part-1/)_._   
> _And the_ [_part talking about willUpdateProps is available here_](https://codingdodo.com/realworld-app-with-owl-odoo-web-library-part-3/)_._


### Implementing the code

First, let's extend the **OWL XML template** to add our div that will contain the quote.

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<templates>
    <t t-inherit="web.ControlPanel" t-inherit-mode="extension" owl="1">
        <xpath expr="//div[hasclass('o_control_panel')]" position="inside">
            <div t-esc="state.customText" class="o_control_panel_random_quote"></div>
        </xpath>
    </t>
</templates>
```
<figcaption>/static/src/xml/control_panel.xml</figcaption>

Inheriting an OWL XML Template is very similar to extending standard QWeb templates except that you should not forget to add `owl="1"`. We will put our `div` inside the control panel and show the customText inside the `state` of our Component.

We will make it prettier by adding some custom SCSS for it, inside `/src/scss/control_panel.scss`.

```scss
.o_control_panel {
  .o_control_panel_random_quote {
    color: $text-muted;
    font-style: italic;
    align-items: center;
    justify-content: center;
    font-weight: bolder;
  }
}
```

Now for the JavaScript module itself /src/js/control\_panel.js

```javascript
odoo.define("owl_tutorial.ControlPanel", function (require) {
  "use strict";
  const ControlPanel = require("web.ControlPanel");
  const { useState } = owl.hooks;

  // ConstrolPanel has a patch function thanks to the patchMixin 
  // This is the usual syntax, first argument is the name of our patch.
  ControlPanel.patch("owl_tutorial.ControlPanelCodingDodo", (T) => {
    class ControlPanelPatched extends T {
      constructor() {
        super(...arguments);
        this.state = useState({
          customText: "",
        });
        console.log(this.state);
      }
        
      async willUpdateProps(nextProps) {
        // Don't forget to call the super
        await super.willUpdateProps(nextProps);
        
        let self = this;
        fetch("https://type.fit/api/quotes")
          .then(function (response) {
            return response.json();
          })
          .then(function (data) {
            let quote = data[Math.floor(Math.random() * data.length)];
            // Update the state of the Component
            Object.assign(self.state, {
              customText: `${quote.text} - ${quote.author}`,
            });
          });
      }
    }
    return ControlPanelPatched;
  });
});

```

As you can see, having the Component returned with `patchMixin` makes it very easy to extend it directly, patch its function and add features!

![OWL in Odoo 14 - How to extend and patch existing OWL Components.](https://codingdodo.com/content/images/2021/08/Capture-d-e-cran-2021-08-06-a--15.31.33.png)

Now let's take a loot at non-patchable Components.

* * *

## Non-Patchable Component: Override a regular function with "web.utils" patch.

As of Odoo 14, most of the Components aren't returned with `patchMixin` and if we want to override the content of some Component functions we will use the `web.utils` **patch** function.

### Example: the FileUpload Component.

Inside the **mail** addon the component FileUpload is responsible of handling the input files and the function that interests us is this one:

```javascript
/**
 * @param {FileList|Array} files
 * @returns {Promise}
 */
async uploadFiles(files) {
    await this._unlinkExistingAttachments(files);
    this._createTemporaryAttachments(files);
    await this._performUpload(files);
    this._fileInputRef.el.value = '';
}
```

This Component isn't return wrapped with the `patchMixin` so we will have to use the "web.utils" function patch.

### Describing the functionality

In this example we will change the behavior of the **file upload** inside the **chatter** send message box:

![OWL in Odoo 14 - How to extend and patch existing OWL Components.](https://codingdodo.com/content/images/2021/08/Capture-d-e-cran-2021-08-02-a--15.46.40.png)

We will try to extend the behavior of the FileUpload so it doesn't even try to compute **any file with a size over 10MB**.

### Implementing the code.

This is the content of our JavaScript module file.

```javascript
odoo.define(
  "owl_tutorial/static/src/components/file_uploader/file_uploader.js",
  function (require) {
    "use strict";

    const components = {
      FileUploader: require("mail/static/src/components/file_uploader/file_uploader.js"),
    };

    const { patch } = require("web.utils");

    patch(
      components.FileUploader,
      "owl_tutorial/static/src/components/file_uploader/file_uploader.js",
      {
        // You can add your own functions to the Component.
        getMaxSize() {
          return 10000000;
        },
          
        /**
         * @override
         */
        async uploadFiles(files) {
          for (const file of files) {
            if (file.size > this.getMaxSize()) {
              // Files over 10MB are now rejected
              this.env.services["notification"].notify({
                type: "danger",
                message: owl.utils.escape(
                  `Max file size allowed is 10 MB, This file ${file.name} is too big!`
                ),
              });
              return false;
            }
          }
          return this._super(files);
        },
      }
    );
    console.log(components.FileUploader.prototype);
  }
);

```

With that done we now have a limit of 10MB on the size of the file uploaded, and a little notification warning us. We return `_super` if no file reached the limit.

![OWL in Odoo 14 - How to extend and patch existing OWL Components.](https://codingdodo.com/content/images/2021/08/Capture-d-e-cran-2021-08-02-a--16.02.25.png)

* * *

## Non-patchable Component: Override the "getter" of an OWL Component.

Some time ago I saw a question on the Odoo forums asking to override the `get avatar` getter of the Message component.

I noticed a lot of confusion around that and unfortunately, as we saw in the introduction, there is also an architectural problem with the way the `patch` function is coded in Odoo core.

### Describing the problem

This is the original `get avatar` getter function:

```javascript
/**
 * @returns {string}
 */
get avatar() {
    if (
        this.message.author &&
        this.message.author === this.env.messaging.partnerRoot
    ) {
        return '/mail/static/src/img/odoobot.png';
    } else if (this.message.author) {
        // TODO FIXME for public user this might not be accessible. task-2223236
        // we should probably use the correspondig attachment id + access token
        // or create a dedicated route to get message image, checking the access right of the message
        return this.message.author.avatarUrl;
    } else if (this.message.message_type === 'email') {
        return '/mail/static/src/img/email_icon.png';
    }
    return '/mail/static/src/img/smiley/avatar.jpg';
}
```

This syntax with **a space** between `get` and `avatar` is what we call a [getter function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get).

To see the problem we have to look inside the content of the `web.utils` **patch** function and especially the `applyPatch` function. We can see this condition

```javascript
if (typeof method === "function") {
    //...
}
```

**But doing `typeof` on `avatar` will give us `string` in that case and not `function` type!** So the patch will never get applied, we will have to find another way to hard override this getter function.

We could try to patch the _`components.Message.`_`prototype` instead of the Message class itself but that would also throw an error because the patch function stores a `WeakMap` on top of the file:

```javascript
  const patchMap = new WeakMap();
```

To search and add patched prototype, the lookup is done via a WeakMap this way:

```javascript
patch: function (C, patchName, patch) {
    let metadata = patchMap.get(C.prototype);
    if (!metadata) {
        metadata = {
            origMethods: {},
            patches: {},
            current: [],
        };
        patchMap.set(C.prototype, metadata);
    }
```

So the `C.prototype` will throw an error if the `C` given is already `SomeClass.prototype`.

### Solution 1 - Redefining prototype property.

To quickly solve this problem we will apply standard JavaScript knowledge with Object. **defineProperty** on the **prototype** and change the "avatar" property.

```javascript
odoo.define(
  "owl_tutorial/static/src/components/message/message.js",
  function (require) {
    "use strict";

    const components = {
      Message: require("mail/static/src/components/message/message.js"),
    };

    Object.defineProperty(components.Message.prototype, "avatar", {
      get: function () {
        if (
          this.message.author &&
          this.message.author === this.env.messaging.partnerRoot
        ) {
          // Here we replace the Robot with the better CodingDodo Avatar
          return "https://avatars.githubusercontent.com/u/81346769?s=400&u=614004f5f4dace9b3cf743ee6aa3069bff6659a2&v=4";
        } else if (this.message.author) {
          return this.message.author.avatarUrl;
        } else if (this.message.message_type === "email") {
          return "/mail/static/src/img/email_icon.png";
        }
        return "/mail/static/src/img/smiley/avatar.jpg";
      },
    });
  }
);

```

![OWL in Odoo 14 - How to extend and patch existing OWL Components.](https://codingdodo.com/content/images/2021/08/Capture-d-e-cran-2021-08-02-a--14.29.01.png)

Note that this is pure JavaScript override and no "Odoo magic" will save you here, the super is not called for you and you have to be really careful when doing that. Any other override after yours on the same getter will override yours!


> Using directly **`Object.defineProperty`** works but you are closing the door to future easy extensions from other modules. It is important to keep that in mind as **Odoo is a module-driven Framework**.


### Solution 2 - Putting the `defineProperty` inside the Component `setup` function (overridable).

It would be better if the standard getter would call a regular function called `_get_avatar` that could be overrideable by other modules.

With the `patch` we also cannot override the `constructor` so we will use a function **available on each OWL Component called `setup`.**

`setup` is called at the end of the constructor of an OWL Component and can be overridden, patched, etc

```javascript
const { patch } = require("web.utils");

patch(
  components.Message,
  "owl_tutorial/static/src/components/message/message.js",
  {
    /**
     * setup is run just after the component is constructed. This is the standard
     * location where the component can setup its hooks.
     */
    setup() {
      Object.defineProperty(this, "avatar", {
        get: function () {
          return this._get_avatar();
        },
      });
    },
    /**
     * Get the avatar of the user. This function can be overriden
     *
     * @returns {string}
     */
    _get_avatar() {
      if (
        this.message.author &&
        this.message.author === this.env.messaging.partnerRoot
      ) {
        // Here we replace the Robot with the better CodingDodo Avatar
        return "https://avatars.githubusercontent.com/u/81346769?s=400&u=614004f5f4dace9b3cf743ee6aa3069bff6659a2&v=4";
      } else if (this.message.author) {
        return this.message.author.avatarUrl;
      } else if (this.message.message_type === "email") {
        return "/mail/static/src/img/email_icon.png";
      }
      return "/mail/static/src/img/smiley/avatar.jpg";
    },
  }
);
```

In that way, the function can now be overridden again by another patch in the future.

```javascript
// Can be overriden again now
patch(
  components.Message,
  "another_module/static/src/components/message/message_another_patch.js",
  {
    _get_avatar() {
      let originAvatar = this._super(...arguments);
      console.log("originAvatar", originAvatar);
      if (originAvatar === "/mail/static/src/img/odoobot.png") {
        return "https://avatars.githubusercontent.com/u/81346769?s=400&u=614004f5f4dace9b3cf743ee6aa3069bff6659a2&v=4";
      }
      return originAvatar;
    },
  }
);
```


> Getter and setters are a cool feature of Classes but leave little space for extendability, it is sometimes better to make a getter as a shortcut to an actual classic function **that can be extended later**.


### Solution 3 - Force apply the "patchMixin" on the Component and replace it in the Components tree.

The last solution is to create another Component equal to the old Component returned with `patchMixin`, then replace them where they are used in parent Components.

```javascript
const { QWeb } = owl;
const patchMixin = require("web.patchMixin");

// Create patchable component from original Message
const PatchableMessage = patchMixin(components.Message);
// Get parent Component 
const MessageList = require("mail/static/src/components/message_list/message_list.js");

PatchableMessage.patch(
  "owl_tutorial/static/src/components/message/message.js",
  (T) => {
    class MessagePatched extends T {
      /**
       * @override property
       */
      get avatar() {
        if (
          this.message.author &&
          this.message.author === this.env.messaging.partnerRoot
        ) {
          // Here we replace the Robot with the better CodingDodo Avatar
          return "https://avatars.githubusercontent.com/u/81346769?s=400&u=614004f5f4dace9b3cf743ee6aa3069bff6659a2&v=4";
        } else if (this.message.author) {
          return this.message.author.avatarUrl;
        } else if (this.message.message_type === "email") {
          return "/mail/static/src/img/email_icon.png";
        }
        return "/mail/static/src/img/smiley/avatar.jpg";
      }
    }
    return MessagePatched;
  }
);
MessageList.components.Message = PatchableMessage;
```

We had to import the parent `MessageList` component to redefine its own `components` and put our own `PatchableMessage`.

The good thing is that now, every other module can extend our `PatchableMessage` and override easily our function! 🥳

## Conclusion

In this article, we reviewed the two main available methods of patching, overriding, and extending Odoo 14 OWL Components. The `patch`function available when the Component is returned with the `patchMixin` and the global `patch` function from "web.utils" when we want to override basic functions of a Component.

I hope this guide was helpful to you on your journey customizing OWL Components in Odoo 14. In another article, we will so how to create Odoo 14 OWL Components from scratch and take a look at all the **adapters** available to us to mix OWL Components with good old Odoo Widgets.

**The repository for this tutorial is available here:**

{% github Coding-Dodo/owl_tutorial_extend_override_components %}


Please consider [subscribing](https://codingdodo.com/#/portal/signup) to be alerted when new content is released here on Coding Dodo.

You can also [follow me on Twitter](https://twitter.com/CodingDodo_) and interact with me for requests about the content you would like to see here!

☕️ [Buying me a Coffee](https://www.buymeacoffee.com/CodingDodo)