DEV Community

ixkit
ixkit

Posted on

How to customize the left side menu icon of the Odoo Settings page view

  • Problem or unprovided features:

  • The native Odoo community version 16 provide a unify Settings page view for addons modules, some time we want customize the left side menu item icon in the view, but it is not easy todo since the native code not provide relative interface yet. check issue: allow dynamic icon setting on configuration views

    • #odoo Odoo 16 implement the Setting panel/form use compiler solution, accurate source code file is : ./addons/web/static/src/webclient/settings_form_view/settings_form_compiler.js
    • the key step function code bellows:
      function getAppIconUrl(module) {

          return module === "general_settings"
              ? "/base/static/description/settings.png"
              : "/" + module + "/static/description/icon.png";
      }
Enter fullscreen mode Exit fullscreen mode

the code above hard code that seem can not be customized and extends unless modify the original js code directly ❓❓❓

  • Solution

    • brief:
    • in this article we show a solution that inject new logic code into exist original code make the Setting view menu icons can be customized πŸ”¨
    • original logic workflow :
      • while odoo frontend init (load web html page), workflow will add settings_form_view to registry.category("views") with key 'base_settings',
      • the 'base_settings' use to render the Setting_page view .
      • entry js file: addons/web/static/src/webclient/settings_form_view/settings_form_view.js
      • it declare const value 'settingsFormView' which collect MVC classes for render Settings page .
              export const settingsFormView = {
                ...
                  Model: SettingsRelationalModel,
                  ControlPanel: ControlPanel,
                  Controller: SettingsFormController,
                Compiler: SettingsFormCompiler, // <--- this class is key point
                Renderer: SettingsFormRenderer,
              }
              registry.category("views").add("base_settings", settingsFormView);
    
    • the class SettingsFormCompiler contains a property 'compilers' which is array that keep all relative compilers,
    • the setting tab area (which contains the left side module menus ) was generate by the function compileSettingsPage(el, params) , so then what we can do is replace the exist inner function logic ~

steps:

  • 1πŸ’‘ find the exist settingsFormView.

since the settingsFormView was register in category 'views', the category is inherit from EventBus, EventBus mechanism is Observer pattern that can use to listener 'UPDATE' event while a view class was add into current runtime session, so hook code bellows:

          const viewRegistry = registry.category("views");

          viewRegistry.on("UPDATE", null, function (...args) {
              console.log(...args);
              const {operation,key,value} = args[0];

              if (operation === 'add' && 'base_settings' === key){ 
                  console.log('βš’οΈ  inject base_settings, key?, value?', key, value); 
                  on_hook(value);
              }

          });

          function on_hook(settingsFormView){ 
              console.log('βš’οΈ onHook',settingsFormView)
          }
Enter fullscreen mode Exit fullscreen mode

start running the frontend code ( launcher Odoo python server, open web browser, navigate to odoo http entry point ), we use console check the log.
the console logout the class view 'settingsFormView' , most like bellows:
Image description

you see that is easy to intercept any view class that was defined base on owl framework❗️ βœ…
the settingsFormView use to render the setting page in Odoo, the attribute 'Compiler' is the class 'SettingsFormCompiler' ('defined in file: settings_form_compiler.js')

  • now we got the target : settingsFormView, next step πŸ”¨ it, we need redefine the attribute 'Compiler' of the value settingsFormView with new class SetingCustomizeAbleCompiler
              function on_hook(settingsFormView){ 
                  console.log('βš’οΈ onHook',settingsFormView)
                  const settingsFormCompiler = settingsFormView.Compiler;

                  if (settingsFormCompiler){
                      settingsFormView.Compiler = SetingCustomizeAbleCompiler;
                      return ;
                  } 
              }      
Enter fullscreen mode Exit fullscreen mode
  • 2πŸ’‘ override class SettingsFormCompiler apply with new log - We declare new class CustomizeAbleSettingsFormCompiler bellows
              class CustomizeAbleSettingsFormCompiler extends SettingsFormCompiler {

                  setup() {
                      super.setup(); 
                      console.debug('inherited, this?',  this);

                      const list = this.compilers;
                      for (let index = 0; index < list.length; index++) {
                          const item = list[index];
                          //match to:  { selector: "div.settings", fn: compileSettingsPage },
                          if (item.selector === "div.settings"){ 
                              const original_hanlder = item.fn;
                              item.fn = compileSettingsPage; // assgin with new logic handler ~
                          }
                      }
                  }
              }
Enter fullscreen mode Exit fullscreen mode

notice above code, we only need hack the selector 'div.settings' , because the routine duty to generate the 'settings_tab' view that render the left side menu area, the function compileSettingsPage should be copy from original source code.
key step to make the menu icon can be configuration :

```
          //@ modify logic here 
          function getAppIconUrl(module) {
              console.log('βš’οΈ getAppIconUrl, module?', module)
              // apply new logic 
              let value = getMenuIcon(module);
              if (value) {
                  return value;
              }
              // continue old logic
              return module === "general_settings"
                  ? "/base/static/description/settings.png"
                  : "/" + module + "/static/description/icon.png";
          }

          function getMenuIcon(name){
            if (name === 'purchase'){
                return '/myModule/static/src/icon/purchase.png'
              }
                if (name === 'stock'){
                return '/myModule/static/src/icon/stock.png'
              }  
              // add more configuration here 

              return null;
          }
```
Enter fullscreen mode Exit fullscreen mode
  • 3πŸ’‘ finally run see the update
    - if lucky then the left side menu icon was updated as expectation:
    - success

  • Conclusion

    • Odoo offered a great data driver framework for ERP system, that is rapid development even low code solution, in this article we give a tips for customize the SettingView, the idea also can apply to other even whole page view in frontend development of owl.
    • Hope this is helpful, if you have more better idea or suggestion, feel free leave comment or contact with us .

Happy coding and fun πŸ‘

Top comments (0)