OWL Field
In this section we are going to create a field widget and display it inside the form
view. Here we are adding a color picker widget and saving the values of picked color in
an integer field. Next we can go through the steps needed to add a new OWL field
component to the form view.
First adding an integer field inside the res.partner,
from odoo import fields, models
class ResPartner(models.Model):
_inherit = 'res.partner'
color = fields.Integer()
Then add the color field to the form view,
<field name="color" widget="int_color"/>
Next we defining the Qweb Template for the field widget,
<?xml version="1.0" encoding="UTF-8" ?>
<templates>
<t t-name="OWLColorPill" owl="1">
<span t-attf-class="o_color_pill o_color_{{props.pill_no}}
{{props.active and 'active' or ''}}"
t-att-data-val="props.pill_no" t-on-click="pillClicked"
t-attf-title="This color is used in {{props.book_count or 0 }} partners."/>
</t>
<span t-name="OWLFieldColorPills" owl="1" class="o_int_colorpicker" t-on-color-updated="colorUpdated">
<t t-foreach="totalColors" t-as='pill_no'>
<ColorPill t-if="mode === 'edit' or value == pill_no" pill_no='pill_no' active='value == pill_no'
book_count="colorGroupData[pill_no]"/>
</t>
</span>
</templates>
Loading the Qweb template to the manifest,
'assets': {
'web.assets_backend': [
'/owl_test/static/src/js/owl_test.js',
],
'web.assets_qweb': [
'/owl_test/static/src/xml/owl_test.xml',
],
},
In next step we are going to add some css style for the field widget,
.o_int_colorpicker {
.o_color_pill {
display: inline-block;
height: 25px;
width: 25px;
margin: 4px;
border-radius: 25px;
position: relative;
@for $size from 1 through length($o-colors) {
&.o_color_#{$size - 1} {
background-color: nth($o-colors, $size);
&:not(.readonly):hover {
transform: scale(1.2);
transition: 0.3s;
cursor: pointer;
}
&.active:after{
content: "\f00c";
display: inline-block;
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
color: #fff;
position: absolute;
padding: 4px;
font-size: 16px;
}
}
}
}
}
Adding the scss file to the backend,
'assets': {
'web.assets_backend': [
'/owl_test/static/src/js/owl_test.js',
'/owl_test/static/src/scss/style.scss'
],
'web.assets_qweb': [
'/owl_test/static/src/xml/owl_test.xml',
],
},
Defining the widget component,
odoo.define('owl_test.owl_test', function (require) {
"use strict";
const { Component } = owl;
const AbstractField = require('web.AbstractFieldOwl');
const fieldRegistry = require('web.field_registry_owl');
class ColorPill extends Component {
static template = 'OWLColorPill';
pillClicked() {
this.trigger('color-updated', {val: this.props.pill_no});
}
}
});
Extend the Abstract field,
class FieldColor extends AbstractField {
static supportedFieldTypes = ['integer'];
static template = 'OWLFieldColorPills';
static components = { ColorPill };
}
Add the methods of the component, then the whole js is as follows:-
odoo.define('owl_test.owl_test', function (require) {
"use strict";
const { Component } = owl;
const AbstractField = require('web.AbstractFieldOwl');
const fieldRegistry = require('web.field_registry_owl');
class ColorPill extends Component {
static template = 'OWLColorPill';
pillClicked() {
this.trigger('color-updated', {val: this.props.pill_no});
}
}
class FieldColor extends AbstractField {
static supportedFieldTypes = ['integer'];
static template = 'OWLFieldColorPills';
static components = { ColorPill };
constructor(...args) {
super(...args);
this.totalColors = Array.from({length: 10}, (_, i) => (i + 1).toString());
}
async willStart() {
this.colorGroupData = {};
var colorData = await this.rpc({
model: this.model, method: 'read_group',
domain: [], fields: ['color'],
groupBy: ['color'],
});
colorData.forEach(res => {
this.colorGroupData[res.color] = res.color_count;
});
}
colorUpdated(ev) {
this._setValue(ev.detail.val);
}
}
fieldRegistry.add('int_color', FieldColor);
return {
FieldColor: FieldColor,
};
});
In the above given example first we have added an integer field to the res.partner model
and then added the field to the form view. Next, we define the Qweb template for the
field widget. When defining an OWL Qweb template, we need to add owl=”1” while defining
the template. Here we are using the idea of subcomponents too. We added two templates,
one for the field and another for the color pill. The <ColorPill> is used to
instantiate the subcomponent. When calling the <ColorPill> tag, we are passing two
attributes named active and pill_no. Those will be the props in the template of the
subcomponent. The t-on-color-updated is used to listen to the custom event triggered
from the subcomponent. Then we added the JS file for the field component. Defining the
OWL utility and also importing the AbstractField and fieldRegistry. AbstrctField is the
abstract OWL component for the fields, and fieldRegistery is used to list the OWL
component as a field component. Then we define the field component named ColorPill. The
static variable template will be the template defined for the component. There is a
method pillCilcked inside the ColorPill component that is called when the user clicks on
the color pill. Inside the method, we have triggered the color-updated event, which is
captured by the FieldColor component. The FieldColor component is the parent component.
The captured event will be used t-on-color-updated on the FieldColor component. By
extending the AbstractField, we created the FieldColor component. Because it will have
all the utilities required to create a field widget. Then we need to list the components
via the static components variable when we use the subcomponents. There is a method
colorUpdated, which will be called when a user clicks on the color pill. The setValue
method is called at the moment for setting the field values that will be stored in the
database. The data triggered will be available at the details attribute of the event
parameter. At last, we registered our widget in the fieldRegistery. The output will be
as follows:-