Development Book V18: New View Creation

Views are essential in Odoo for presenting records in a user-friendly manner. Without a well-structured view, users may struggle to interpret the information effectively. Odoo supports a variety of view types, including form, list, kanban, graph, pivot, calendar, dashboard, search, grid, cohort, and others.

While adding a new view can be complex, this guide highlights the key steps involved in the process.

1. Create the controller.

The primary role of a controller is to coordinate the interaction between various view components, including the Renderer, Model, and Layout.

Create custom_controller.js

                       /** @odoo-module */

import { Layout } from "@web/search/layout";
import { useService } from "@web/core/utils/hooks";
import { Component, onWillStart, useState} from "@odoo/owl";

export class CustomController extends Component {
   setup() {
       this.orm = useService("orm");

       // The controller create the model and make it reactive so whenever this.model is
       // accessed and edited then it'll cause a rerendering
       this.model = useState(
           new this.props.Model(
               this.orm,
               this.props.resModel,
               this.props.fields,
               this.props.archInfo,
               this.props.domain
           )
       );
       onWillStart(async () => {
           await this.model.load();
       });
   }
}

CustomController.template = "custom_view.View";
CustomController.components = { Layout };

The Controller template displays the control panel along with the Layout and the Renderer components.

Create custom_controller.xml

<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
   <t t-name="custom_view.View">
       <Layout display="props.display" className="'h-100 overflow-auto'">
           <t t-component="props.Renderer" records="model.records" propsYouWant="'Hello world'"/>
       </Layout>
   </t>
</templates>

2. Create the renderer.

Create custom_renderer.js

The primary role of a renderer is to visually present data by rendering the view that displays the records.

  /** @odoo-module */

import { Component } from "@odoo/owl";

export class CustomRenderer extends Component {}

CustomRenderer.template = "custom_view.Renderer";

Create custom_renderer.xml

   <?xml version="1.0" encoding="UTF-8"?>
    <templates xml:space="preserve">
       <t t-name="my_module.Renderer">
           <t t-esc="props.propsYouWant"/>
       </t>
    </templates>

3. Create the model.

The model is responsible for fetching and managing all the data needed for the view.

Create custom_model.js

/** @odoo-module */

import { KeepLast } from "@web/core/utils/concurrency";

export class CustomModel {
   constructor(orm, resModel, fields, archInfo, domain) {
       this.orm = orm;
       this.resModel = resModel;
       // We can access arch information parsed by the beautiful arch parser
       const { fieldFromTheArch } = archInfo;
       this.fieldFromTheArch = fieldFromTheArch;
       this.fields = fields;
       this.domain = domain;
       this.keepLast = new KeepLast();
   }

   async load() {
       // The keeplast protect against concurrency call
       const { length, records } = await this.keepLast.add(
           this.orm.webSearchRead(this.resModel, this.domain, [this.fieldsFromTheArch], {})
       );
              this.records = records;
       this.recordsLength = length;
   }
}

4. Create the arch parser.

The arch parser is responsible for parsing the arch (architecture) of the view, enabling the view to interpret and access its defined structure and information.

Create custom_arch_parser.js

/** @odoo-module */

import { XMLParser } from "@web/core/utils/xml";

export class GanttArchParser {
   parse(arch) {
       const xmlDoc = this.parseXML(arch);
       const fieldFromTheArch = xmlDoc.getAttribute("fieldFromTheArch");
       return {
           fieldFromTheArch,
       };
   }
}

5. Create the view by assembling all of its components, and then registering it in the views registry.

Create custom_view.js

/** @odoo-module */

import { registry } from "@web/core/registry";
import { CustomController } from "./custom_controller";
import { CustomArchParser } from "./custom_arch_parser";
import { CustomModel } from "./custom_model";
import { CustomRenderer } from "./custom_renderer";

export const customView = {
   type: "custom_view",
   display_name: "Custom",
   icon: "fa fa-picture-o", // the icon that will be displayed in the Layout panel
   multiRecord: true,
   Controller: CustomController,
   ArchParser: CustomArchParser,
   Model: CustomModel,
   Renderer: CustomRenderer,

   props(genericProps, view) {
       const { ArchParser } = view;
       const { arch } = genericProps;
       const archInfo = new ArchParser().parse(arch);

       return {
           ...genericProps,
           Model: view.Model,
           Renderer: view.Renderer,
           archInfo,
       };
   },
};

registry.category("views").add("custom_view", customView);

6. Add it to the manifest file

'assets': {
   'web.assets_backend': [
       'custom_view/static/src/js/custom_arch_parser.js',
       'custom_view/static/src/js/custom_controller.js',
       'custom_view/static/src/js/custom_model.js',
       'custom_view/static/src/js/custom_renderer.js',
       'custom_view/static/src/js/custom_view.js',
       'custom_view/static/src/xml/custom_controller.xml',
       'custom_view/static/src/xml/custom_renderer.xml',
   ],
},

7. Add the created view to the view mode

   from odoo import fields, models

    class IrActionsActWindowView(models.Model):
   _inherit = 'ir.actions.act_window.view'

   view_mode = fields.Selection(
       selection_add=[('custom_view', "Custom")],
       ondelete={'custom_view': 'cascade'}
   )

8. Add to the type in ir.ui.view

     class IrUiView(models.Model):
   _inherit = 'ir.ui.view'

   type = fields.Selection(
       selection_add=[('custom_view', "Custom")]
   )

Now, let's explore how to add a custom_view to the list of available views. In this example, we'll integrate the custom view into the My Tasks view within Odoo's Project module.

           <record id="project_task_custom_view" model="ir.ui.view" >
   <field name="name">project.task.custom_view</field >
    <field name="model">project.task</field >
    <field name="arch" type="xml" >
        <custom_view/  >
    </field  >
 </record  >
 
 <record id="action_view_all_task"
        model="ir.actions.act_window"  >
    <field name="view_mode">kanban,tree,form,custom_view </field  >
 </record >
whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, KINFRA Techno Park
Kakkanchery, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message