Un composant Owl vit dans le navigateur, à l'intérieur du client web Odoo. La view reste plus simple pour une fiche classique. Le composant devient intéressant lorsqu'on veut un écran très personnalisé qui se rafraîchit sans recharger toute la page.
Les extraits utilisent title et body. Gardez ces mêmes noms techniques dans le JavaScript et le template Owl.
1. Lecture seule : le service ORM fournit les notes
import { Component, onWillStart, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
export class TrainingNotes extends Component {
static template = "training_hello.TrainingNotes";
setup() {
this.orm = useService("orm");
this.state = useState({ notes: [] });
onWillStart(() => this.loadNotes());
}
async loadNotes() {
this.state.notes = await this.orm.searchRead(
"training.note", [], ["title", "body"]
);
}
}
registry.category("actions").add("training_hello.training_notes", TrainingNotes);<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="training_hello.TrainingNotes">
<div class="o_action p-4">
<h1>Notes de formation</h1>
<ul>
<li t-foreach="state.notes" t-as="note" t-key="note.id">
<strong t-esc="note.title"/>
<p t-esc="note.body"/>
</li>
</ul>
</div>
</t>
</templates>Déclarez les deux fichiers comme assets et reliez le tag enregistré en JavaScript à une client action. Cette action peut être ajoutée au fichier XML qui contient déjà les menus des notes :
web.assets_backend"web.assets_backend": [
# ... les assets existants
"training_hello/static/src/training_notes/training_notes.js",
"training_hello/static/src/training_notes/training_notes.xml",
],<record id="training_notes_client_action" model="ir.actions.client">
<field name="name">Notes Owl</field>
<field name="tag">training_hello.training_notes</field>
</record>
<menuitem id="training_notes_owl_menu" name="Notes Owl"
parent="training_note_menu_root"
action="training_notes_client_action" sequence="30"/>2. Rendre le composant capable de créer une note
Ajoutez un état pour les valeurs du form, puis appelez le même ORM Odoo. Le navigateur ne choisit aucun accès supplémentaire : orm.create() arrive à Odoo, qui applique les droits du même utilisateur connecté.
setup(), puis ajoutez la méthode à la classesetup() {
this.orm = useService("orm");
this.state = useState({ notes: [], title: "", body: "" });
onWillStart(() => this.loadNotes());
}
async createNote() {
const title = this.state.title.trim();
if (!title) {
return;
}
await this.orm.create("training.note", [{
title,
body: this.state.body,
}]);
this.state.title = "";
this.state.body = "";
await this.loadNotes();
}<form t-on-submit.prevent="createNote">
<label>Titre <input t-model="state.title" required="required"/></label>
<label>Contenu <textarea t-model="state.body"></textarea></label>
<button type="submit" class="btn btn-primary">Enregistrer</button>
</form>La view interne utilise directement les mécanismes standard d'Odoo. Le template QWeb passe par un contrôleur Python. Le composant Owl passe par le service ORM du client web. Dans les trois cas, ce sont le model, l'ACL et l'ORM Odoo qui protègent et enregistrent les données dans PostgreSQL. Le choix concerne l'expérience à construire, pas la permission de contourner les règles.