<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ixkit</title>
    <description>The latest articles on DEV Community by ixkit (@ixkit).</description>
    <link>https://dev.to/ixkit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1344620%2F236a5db8-f331-4e33-97ec-b5acf4129a0f.png</url>
      <title>DEV Community: ixkit</title>
      <link>https://dev.to/ixkit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ixkit"/>
    <language>en</language>
    <item>
      <title>good👍</title>
      <dc:creator>ixkit</dc:creator>
      <pubDate>Wed, 30 Apr 2025 06:07:03 +0000</pubDate>
      <link>https://dev.to/ixkit/good-1dkd</link>
      <guid>https://dev.to/ixkit/good-1dkd</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/treekon_ventures/streamlined-odoo-18-development-with-docker-compose-a-developers-guide-4ph1" class="crayons-story__hidden-navigation-link"&gt;Streamlined Odoo 18 Development with Docker Compose: A Developer's Guide&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/treekon_ventures" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2990225%2Fae5651e1-c172-4f0b-8ec0-888747d35e9e.png" alt="treekon_ventures profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/treekon_ventures" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Treekon Ventures
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Treekon Ventures
                
              
              &lt;div id="story-author-preview-content-2366505" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/treekon_ventures" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2990225%2Fae5651e1-c172-4f0b-8ec0-888747d35e9e.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Treekon Ventures&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/treekon_ventures/streamlined-odoo-18-development-with-docker-compose-a-developers-guide-4ph1" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 30 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/treekon_ventures/streamlined-odoo-18-development-with-docker-compose-a-developers-guide-4ph1" id="article-link-2366505"&gt;
          Streamlined Odoo 18 Development with Docker Compose: A Developer's Guide
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/odoo"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;odoo&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/beginners"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;beginners&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/treekon_ventures/streamlined-odoo-18-development-with-docker-compose-a-developers-guide-4ph1#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>odoo</category>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>A hook solution to spy the Odoo web page 🕵️</title>
      <dc:creator>ixkit</dc:creator>
      <pubDate>Sat, 13 Apr 2024 07:30:15 +0000</pubDate>
      <link>https://dev.to/ixkit/a-hook-solution-to-spy-the-odoo-web-page-309p</link>
      <guid>https://dev.to/ixkit/a-hook-solution-to-spy-the-odoo-web-page-309p</guid>
      <description>&lt;p&gt;&lt;em&gt;Help you insight into the rendering Templates of the Odoo web page,light up the template snippets,speed up development process🚀&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Odoo rendering a web page
&lt;/h2&gt;

&lt;p&gt;Odoo is an Open source ERP software, also an rapid development web application framework, the Odoo main tech stack includes Python module and JavaScript framework, we think that two type rendering technique was used: &lt;strong&gt;QWeb&lt;/strong&gt; and &lt;strong&gt;OWL&lt;/strong&gt;. &lt;br&gt;
👉 &lt;a href="https://www.odoo.com/documentation/17.0/developer/reference/frontend/qweb.html" rel="noopener noreferrer"&gt;QWeb&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;QWeb is the primary templating engine used by Odoo. It is an XML templating engine1 and used mostly to generate HTML fragments and pages.&lt;br&gt;
   Odoo HTTP layer base on python WSGI, the web page render  on server side, most like other language solution, eg PHP echo or JSP that allows dynamic content injection into static contents, the static contents call "&lt;a href="https://www.odoo.com/documentation/13.0/developer/reference/javascript/qweb.html" rel="noopener noreferrer"&gt;Template&lt;/a&gt;",  a pice template code like bellows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template id="login_layout" inherit_id="web.login_layout" name="Website Login Layout" priority="20"&amp;gt;
    &amp;lt;xpath expr="t" position="replace"&amp;gt;
        &amp;lt;t t-call="website.layout"&amp;gt;
            &amp;lt;div class="oe_website_login_container" t-out="0"/&amp;gt;
        &amp;lt;/t&amp;gt;
    &amp;lt;/xpath&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;above code mixed two type scripts: xml + html, it define the page layout of the login page which use for end user from web browser, eg while user try to login the odoo application from the link '&lt;a href="http://localhost:8120/web/login" rel="noopener noreferrer"&gt;http://localhost:8120/web/login&lt;/a&gt;', the ir_qweb.py duty to render the path page, the web page show bellow screen snapshot&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp879fra8uwlcswhg01mf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp879fra8uwlcswhg01mf.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We know that the page was rendering by many different template snippets, the page HTML elements was generated by a couple of QWeb templates,  "t-call" or  "inherit_id" , such operation primitive organize many different location template script into one big virtual template page that will be process by QWeb engine, top-level rendering probably contains many sub-templates that is spread out in different xml file, it is not easy to trace or debug or modify.&lt;br&gt;&lt;br&gt;
 Limited the QWeb Templates syntax rule, it does not contains meta information like template location(path), the templating engine also does not provide feature to trace each template location(path) in an rendering session, this is a big(maybe not :D) question for developer.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/odoo/owl" rel="noopener noreferrer"&gt;OWL&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The official definition: OWL, A web framework for structured, dynamic and maintainable applications. it also use &lt;a href="https://github.com/odoo/owl/blob/master/doc/reference/templates.md" rel="noopener noreferrer"&gt;QWeb template&lt;/a&gt; as layout vector to rendering the web page, the problem still exist, if a web page includes many different Owl components, it is not easy to get the component template location on the disk, why we need know the location (path), because we need modify it or review the rendering logic as well.  &lt;/p&gt;

&lt;p&gt;🔥&lt;strong&gt;Shortcoming of template rendering engine&lt;/strong&gt;&lt;br&gt;
 if the page is complex that use variant templates, then it is hard to know current web page component, we need insight the page structure,  We often offer lot of time to find the template snippets code on disk, why we need waste time to search by keyword or memory, it must be another way to easy locate the template from web page directly, just one click⚡️   &lt;/p&gt;
&lt;h2&gt;
  
  
  Solution 🔨
&lt;/h2&gt;

&lt;p&gt;so far, we only provide a hook solution to append the meta data (especially the location/path information) of the template while Odoo rendering the web page. &lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;QWeb&lt;/strong&gt;&lt;br&gt;
  the ir_qweb.py file duty rendering the QWeb template in Python part, the official code comment bellows:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```.. code-block:: rst&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Odoo
 ┗━► _render (returns MarkupSafe)
    ┗━► _compile (returns function)                                        ◄━━━━━━━━━━┓
       ┗━► _compile_node (returns code string array)                       ◄━━━━━━━━┓ ┃
          ┃  (skip the current node if found t-qweb-skip)                           ┃ ┃
          ┃  (add technical directives: t-tag-open, t-tag-close, t-inner-content)   ┃ ┃
          ┃                                                                         ┃ ┃
          ┣━► _directives_eval_order (defined directive order)                      ┃ ┃
          ┣━► _compile_directives (loop)    Consume all remaining directives ◄━━━┓  ┃ ┃
          ┃  ┃                              (e.g.: to change the indentation)    ┃  ┃ ┃
          ┃  ┣━► _compile_directive                                              ┃  ┃ ┃
          ┃  ┃    ┗━► t-nocache       ━━► _compile_directive_nocache            ━┫  ┃ ┃
          ┃  ┃    ┗━► t-cache         ━━► _compile_directive_cache              ━┫  ┃ ┃
          ┃  ┃    ┗━► t-groups        ━━► _compile_directive_groups             ━┫  ┃ ┃
          ┃  ┃    ┗━► t-foreach       ━━► _compile_directive_foreach            ━┫  ┃ ┃
          ┃  ┃    ┗━► t-if            ━━► _compile_directive_if                 ━┛  ┃ ┃
          ┃  ┃    ┗━► t-inner-content ━━► _compile_directive_inner_content ◄━━━━━┓ ━┛ ┃
          ┃  ┃    ┗━► t-options       ━━► _compile_directive_options             ┃    ┃
          ┃  ┃    ┗━► t-set           ━━► _compile_directive_set           ◄━━┓  ┃    ┃
          ┃  ┃    ┗━► t-call          ━━► _compile_directive_call            ━┛ ━┫ ━━━┛
          ┃  ┃    ┗━► t-att           ━━► _compile_directive_att                 ┃
          ┃  ┃    ┗━► t-tag-open      ━━► _compile_directive_open          ◄━━┓  ┃
          ┃  ┃    ┗━► t-tag-close     ━━► _compile_directive_close         ◄━━┫  ┃
          ┃  ┃    ┗━► t-out           ━━► _compile_directive_out             ━┛ ━┫ ◄━━┓
          ┃  ┃    ┗━► t-field         ━━► _compile_directive_field               ┃   ━┫
          ┃  ┃    ┗━► t-esc           ━━► _compile_directive_esc                 ┃   ━┛
          ┃  ┃    ┗━► t-*             ━━► ...                                    ┃
          ┃  ┃                                                                   ┃
          ┗━━┻━► _compile_static_node                                           ━┛```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like the comment, it is great, , so maybe we can do something according the guide :-)&lt;br&gt;
 the class IrQWeb duty to parse the template and rendering data to HTML, the method _load(self, ref) use to read/load template code, so what we do just append the template location/path information to the original template in the method _load().&lt;/p&gt;

&lt;p&gt;let use a example to demo the key steps:&lt;br&gt;
A template 'brand_promotion' that use to display the odoo brand on Home web page, the code bellows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template id="brand_promotion" name="Brand Promotion"&amp;gt;
        &amp;lt;div class="o_brand_promotion"&amp;gt;
            &amp;lt;t t-call="web.brand_promotion_message"&amp;gt;
                &amp;lt;t t-set="_message"&amp;gt;&amp;lt;/t&amp;gt;
                &amp;lt;t t-set="_utm_medium" t-valuef="portal"/&amp;gt;
            &amp;lt;/t&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/template&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the template rendering effect on web page screen snapshot as bellow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6hvrmmpkpyj4sr0pv24.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6hvrmmpkpyj4sr0pv24.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;the template 'brand_promotion' snippets rendering effect on the whole web page :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3yf7g0l1wdu87gatpwb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3yf7g0l1wdu87gatpwb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want it can be spy or self explained meta information like tooltip,   Odoo also provide the developer mode for debug, it can display some button or web component, but the tooltip information is limited even helpless, let's make it more prefect 🔨&lt;/p&gt;

&lt;p&gt;Our solution is append new element spy div wrap the original element, the new element div duty attach the template meta information that can be use for tooltip view.&lt;/p&gt;

&lt;p&gt;the result of the html snippets most like bellows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div
  id="spy-&amp;gt;web.brand_promotion"
  data-tooltip-template="spy.TooltipView"
  data-spy="qweb"
  data-tooltip-info=
        '{ 
            "debug": true,
            "class": "ir.ui.view",
            "view": { 
                "id": 183,
                "name": "Brand Promotion",
                "model": false,
                "key": "web.brand_promotion",
                "type": "qweb",
                "mode": "primary",
                "arch_fs": "web/views/webclient_templates.xml",
                "children_views": [ {
                    "id": 507, "name": "Brand Promotion", "model": false, "key": "website.brand_promotion", "type": "qweb", "mode": "extension", "arch_fs": "website/views/website_templates.xml"
                } 
                ],
                "template_code": "&amp;lt;t name=&amp;amp;amp;?quoteBrand Promotion&amp;amp;amp;?quote t-name=&amp;amp;amp;?quoteweb.brand_promotion&amp;amp;amp;?quote&amp;gt;\n        &amp;lt;div class=&amp;amp;amp;?quoteo_brand_promotion&amp;amp;amp;?quote&amp;gt;\n            &amp;lt;t t-call=&amp;amp;amp;?quoteweb.brand_promotion_message&amp;amp;amp;?quote&amp;gt;\n            &amp;lt;t t-set=&amp;amp;amp;?quote_message&amp;amp;amp;?quote&amp;gt;\n                Create a &amp;lt;a target=&amp;amp;amp;?quote_blank&amp;amp;amp;?quote href=&amp;amp;amp;?quotehttp://www.odoo.com/app/website?utm_source=db&amp;amp;amp;amp;utm_medium=website&amp;amp;amp;?quote&amp;gt;free website&amp;lt;/a&amp;gt;\n            &amp;lt;/t&amp;gt;\n            &amp;lt;t t-set=&amp;amp;amp;?quote_utm_medium&amp;amp;amp;?quote t-valuef=&amp;amp;amp;?quotewebsite&amp;amp;amp;?quote/&amp;gt;\n        &amp;lt;/t&amp;gt;\n    &amp;lt;/div&amp;gt;\n    &amp;lt;/t&amp;gt;"
            }
          }' 

&amp;gt;
  &amp;lt;div class="o_brand_promotion"&amp;gt;
     ... original html code ...
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;let's see the last render effect while appended the meta tooltip view, it show popover tip view while mouse focus the element on web page bellows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rxbzw9ruepyk28a1cnw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rxbzw9ruepyk28a1cnw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;aha, that is what we want, while mouse moveover the brand element, a tooltip view popup, it disply key meta information of the source template of the element: template name, location(arch_fs) etc, we can insight the web page now! the page element is self-explanatory, especial show the location/path information, it should very helpful for debug or extends while developing application base the Odoo framework.   &lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;OWL&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The frontend web page of the Odoo backend (no webiste module) based on OWL framework, the code 'addons/web/static/lib/owl/owl.js' is the core logic set,  the class 'TemplateSet' also 'App' which duty to parse the template script and generate the dom object in web browser side, the method '_compileTemplate' of the class 'TemplateSet' duty to process the template, we can hook the method for our purpose: append meta information on the template snippets.&lt;/p&gt;

&lt;p&gt;the effect bellows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fixkit.com%2Fweb%2Fimage%2F110142-4d7306fc%2Fbackend.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fixkit.com%2Fweb%2Fimage%2F110142-4d7306fc%2Fbackend.gif" alt="spy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;it works good as expectation ✅&lt;/p&gt;

&lt;p&gt;if you like it you can find it from the &lt;a href="https://apps.odoo.com/apps/modules/17.0/kit_spy/" rel="noopener noreferrer"&gt;Odoo Appstore&lt;/a&gt; &lt;/p&gt;






&lt;center&gt;Enjoy &amp;amp; Happy Coding!  ☺︎&lt;center&gt;
&lt;br&gt;
&lt;/center&gt;
&lt;br&gt;
&lt;/center&gt;

</description>
      <category>odoo</category>
      <category>python</category>
      <category>erp</category>
      <category>template</category>
    </item>
    <item>
      <title>How to customize the left side menu icon of the Odoo Settings page view</title>
      <dc:creator>ixkit</dc:creator>
      <pubDate>Sat, 16 Mar 2024 16:20:57 +0000</pubDate>
      <link>https://dev.to/ixkit/how-to-customize-the-left-side-menu-icon-of-the-odoo-settings-page-view-lja</link>
      <guid>https://dev.to/ixkit/how-to-customize-the-left-side-menu-icon-of-the-odoo-settings-page-view-lja</guid>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;h2&gt;
  
  
  Problem or unprovided features:
&lt;/h2&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;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: &lt;a href="https://github.com/odoo/odoo/issues/51659"&gt;allow dynamic icon setting on configuration views&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;#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&lt;/li&gt;
&lt;li&gt;the key step function code bellows:
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      function getAppIconUrl(module) {

          return module === "general_settings"
              ? "/base/static/description/settings.png"
              : "/" + module + "/static/description/icon.png";
      }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the code above hard code that seem can not be customized and extends unless modify the original js code directly ❓❓❓&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt; brief: &lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;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 🔨&lt;/li&gt;
&lt;li&gt;original logic workflow :

&lt;ul&gt;
&lt;li&gt;while odoo frontend init (load web html page), workflow will add settings_form_view  to  registry.category("views") with key 'base_settings',&lt;/li&gt;
&lt;li&gt;the 'base_settings' use to render the Setting_page view .&lt;/li&gt;
&lt;li&gt;entry js file: addons/web/static/src/webclient/settings_form_view/settings_form_view.js&lt;/li&gt;
&lt;li&gt;it declare const value  'settingsFormView' which collect MVC classes for render Settings page .
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          export const settingsFormView = {
            ...
              Model: SettingsRelationalModel,
              ControlPanel: ControlPanel,
              Controller: SettingsFormController,
            Compiler: SettingsFormCompiler, // &amp;lt;--- this class is key point
            Renderer: SettingsFormRenderer,
          }
          registry.category("views").add("base_settings", settingsFormView);
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;the  class &lt;strong&gt;SettingsFormCompiler&lt;/strong&gt; contains a property 'compilers'  which is array that keep all relative compilers,&lt;/li&gt;
&lt;li&gt;the  setting tab area (which contains the left side module menus ) was generate by the function &lt;strong&gt;compileSettingsPage(el, params)&lt;/strong&gt; ,   so then what we can do is replace the exist inner function logic ~&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  steps:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1💡 find the exist settingsFormView&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          const viewRegistry = registry.category("views");

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

              if (operation === 'add' &amp;amp;&amp;amp; 'base_settings' === key){ 
                  console.log('⚒️  inject base_settings, key?, value?', key, value); 
                  on_hook(value);
              }

          });

          function on_hook(settingsFormView){ 
              console.log('⚒️ onHook',settingsFormView)
          }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;start running the frontend code ( launcher Odoo python server, open web browser, navigate to odoo http entry point ),  we use console check the log.&lt;br&gt;
    the console logout the class view 'settingsFormView' , most like bellows:&lt;br&gt;
    &lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdzmv9rmjberekneug7w6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdzmv9rmjberekneug7w6.png" alt="Image description" width="800" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;you see that is easy to intercept any view class that was defined base on owl framework❗️ ✅&lt;br&gt;
    the settingsFormView use to render the setting page in Odoo, the attribute 'Compiler' is the class 'SettingsFormCompiler' ('defined in file: settings_form_compiler.js')&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;now we got the target : &lt;strong&gt;&lt;em&gt;settingsFormView&lt;/em&gt;&lt;/strong&gt;,  next step 🔨 it,  we need redefine the attribute 'Compiler' of the value settingsFormView with  new class SetingCustomizeAbleCompiler
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              function on_hook(settingsFormView){ 
                  console.log('⚒️ onHook',settingsFormView)
                  const settingsFormCompiler = settingsFormView.Compiler;

                  if (settingsFormCompiler){
                      settingsFormView.Compiler = SetingCustomizeAbleCompiler;
                      return ;
                  } 
              }      
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;2💡 override  class SettingsFormCompiler apply with new log&lt;/strong&gt;
        - We declare new class CustomizeAbleSettingsFormCompiler bellows
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              class CustomizeAbleSettingsFormCompiler extends SettingsFormCompiler {

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

                      const list = this.compilers;
                      for (let index = 0; index &amp;lt; 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 ~
                          }
                      }
                  }
              }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;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.&lt;br&gt;
  key step to make the menu icon can be configuration :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
          //@ 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;
          }
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;3💡 finally run see the update&lt;/strong&gt;&lt;br&gt;
        - if lucky then the left side menu icon was updated as expectation:&lt;br&gt;
        - &lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4prtgkwo04oidfzbced.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4prtgkwo04oidfzbced.png" alt="success" width="800" height="603"&gt;&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;li&gt;Hope this is helpful, if you have more better idea or suggestion, feel free leave comment or contact with us .&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding and fun 👏 &lt;/p&gt;

</description>
      <category>odoo</category>
      <category>javascript</category>
      <category>erp</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
