Chapter 1: Fields and Views¶
In the previous chapter, we learned a range of skills, including how to create and use services, work with the Layout component, make the dashboard translatable, and lazy load a JavaScript library like Chart.js. Now, let’s learn how to create new fields and views.
The solutions for each exercise of the chapter are hosted on the official Odoo tutorials repository. It is recommended not to look at them before trying the exercises.
Fields and views are among the most important concepts in the Odoo user interface. They are key to
many important user interactions, and should therefore work perfectly. In the context of the
JavaScript framework, fields are components specialized for visualizing/editing a specific field for
a given record. For example, a (Python) model may define a char field, which will be represented by
a field component CharField
.
A field component is basically just a component registered in the fields
registry. The field component may define some additional static keys (metadata), such
as displayName
or supportedTypes
, and the most important one: extractProps
, which prepare the
base props received by the CharField
.
Example: a simple field¶
Let us discuss a simplified implementation of a CharField
. First, here is the template:
<t t-name="web.CharField" owl="1">
<t t-if="props.readonly">
<span t-esc="formattedValue" />
</t>
<t t-else="">
<input
class="o_input"
t-att-type="props.isPassword ? 'password' : 'text'"
t-att-placeholder="props.placeholder"
t-on-change="updateValue"
/>
</t>
</t>
It features a readonly mode and an edit mode, which is an input with a few attributes. Now, here is the JavaScript code:
export class CharField extends Component {
get formattedValue() {
return formatChar(this.props.value, { isPassword: this.props.isPassword });
}
updateValue(ev) {
let value = ev.target.value;
if (this.props.shouldTrim) {
value = value.trim();
}
this.props.update(value);
}
}
CharField.template = "web.CharField";
CharField.displayName = _lt("Text");
CharField.supportedTypes = ["char"];
CharField.extractProps = ({ attrs, field }) => {
return {
shouldTrim: field.trim && !archParseBoolean(attrs.password),
maxLength: field.size,
isPassword: archParseBoolean(attrs.password),
placeholder: attrs.placeholder,
};
};
registry.category("fields").add("char", CharField);
There are a few important things to notice:
The
CharField
receives its (raw) value in props. It needs to format it before displaying it.It receives an
update
function in its props, which is used by the field to notify the owner of the state that the value of this field has been changed. Note that the field does not (and should not) maintain a local state with its value. Whenever the change has been applied, it will come back (possibly after an onchange) by the way of the props.It defines an
extractProps
function. This is a step that translates generic standard props, specific to a view, to specialized props, useful to the component. This allows the component to have a better API, and may make it so that it is reusable.
Fields have to be registered in the fields
registry. Once it’s done, they can be used in some
views (namely: form
, list
, kanban
) by using the widget
attribute.
Example
<field name="preview_moves" widget="account_resequence_widget"/>
1. An image_preview
field¶
Each new order on the website will be created as an awesome_tshirt.order
. This model has a
image_url
field (of type char
), which is currently only visible as a string. We want to be able
to see the image itself in the form view.
Exercise
Create a new
ImagePreview
component and register it in the proper registry. Use theCharField
component in your template. You can use t-props to pass props received byImagePreview
toCharField
. Update the arch of the form view to use your new field by setting thewidget
attribute.Change the code of the
ImagePreview
component so that the image is displayed below the URL.When the field is readonly, only the image should be displayed and the URL should be hidden.
Note
It is possible to solve this exercise by inheriting CharField
, but the goal of this exercise is
to create a field from scratch.
See also
2. Improving the image_preview
field¶
We want to improve the field of the previous task to help the staff recognize orders for which some action should be done.
Exercise
Display a warning “MISSING TSHIRT DESIGN” in red if there is no image URL specified on the order.
3. Customizing a field component¶
Let’s see how to use inheritance to extend an existing component.
There is a is_late
, readonly, boolean field on the order model. That would be useful information
to see on the list/kanban/view. Then, let us say that we want to add a red word “Late!” next to it
whenever it is set to true
.
Exercise
Create a new
LateOrderBoolean
field inheriting fromBooleanField
. The template ofLateOrderBoolean
can also inherit from theBooleanField
template.Use it in the list/kanban/form view.
Modify it to add a red
Late
next to it, as requested.
4. Message for some customers¶
Odoo form views support a widget
API, which is like a field, but more generic. It is useful to
insert arbitrary components in the form view. Let us see how we can use it.
Exercise
For a super efficient workflow, we would like to display an alert block with specific messages depending on some conditions:
If the
image_url
field is not set, it should display “No image”.If the
amount
of the order is higher than 100 euros, it should display “Add promotional material”.Make sure that your widget is updated in real time.
Tip
Try to evaluate props.record
in the Console tab of your browser’s dev tools.
5. Use markup
¶
Let’s see how we can display raw HTML in a template. The t-out
directive can be used for that
propose. Indeed, it generally acts like t-esc, unless the data has been marked explicitly with a
markup function. In that case, its value
is injected as HTML.
Exercise
Modify the previous exercise to put the
image
andmaterial
words in bold.The warnings should be markuped, and the template should be modified to use
t-out
.Import the
markup
function from Owl and, for each message, replace it with a call of the function with the message passed as argument.
Note
This is an example of a safe use of t-out
, since the string is static.
7. Auto-reload the kanban view¶
Bafien is upset: he wants to see the kanban view of the tshirt orders on his external monitor, but the view needs to be up-to-date. He is tired of clicking on the refresh icon every 30s, so he tasked you to find a way to do it automatically.
Just like the previous exercise, that kind of customization requires creating a new JavaScript view.
Exercise
Extend the kanban view/controller to reload its data every minute.
Register it in the view registry, under
awesome_tshirt.autoreloadkanban
.Use it in the arch of the kanban view (with the
js_class
attribute).
Important
If you use setInterval
or something similar, make sure that it is properly canceled when your
component is unmounted. Otherwise, you will introduce a memory leak.