How to customize the left side menu icon of the Odoo Settings page view
Tags: odoo, javascript, erp, webdev
Published: 2024-03-16 16:20:57


- ## 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](https://github.com/odoo/odoo/issues/51659)
	- #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";
	  }
```
 the code above hard code that seem can not be customized and extends unless modify the original js code directly ❓❓❓
- ## Solution
	- <strong> brief: </strong>
	- 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)
			  }
	 

  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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzmv9rmjberekneug7w6.png)
 
  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 ;
			      } 
			  }		 
```			

- __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 ~
			              }
			          }
			      }
			  }
```
  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;
			  }
	```



- __3💡 finally run see the update__
			- if lucky then the left side menu icon was updated as expectation:
			- ![success](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h4prtgkwo04oidfzbced.png) 
 
- ## 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 👏