Đến giờ ta viết Python phía server. Nhưng giao diện Odoo 19 chạy trong trình duyệt bằng một framework riêng: OWL (Odoo Web Library) — kiểu React/Vue nhưng của Odoo. Khi bạn muốn một màn hình tự vẽ thay vì list/form có sẵn — một dashboard, một widget tương tác — bạn viết một component OWL. Bài này giải phẫu component đầu tiên: một bảng điều khiển lấy số liệu thật và hiển thị.
Ba mảnh của một component
Một component OWL trong Odoo luôn gồm ba phần rời nhau, phải khớp tên với nhau:

Hình 1: Ba mảnh. (1) Lớp JS — class extends Component, khai static template và một setup(). (2) Template QWeb-OWL (.xml) — HTML kèm chỉ thị t-esc/t-if/t-on-click, đọc dữ liệu từ state. (3) Đăng ký — đưa component vào registry để Odoo biết tag nào ứng với component nào. Cả ba phải nằm trong bundle web.assets_backend khai ở manifest; đổi xong phải hard refresh trình duyệt.
Lớp JS: setup, state, và ORM
Đây là trái tim của component — không có constructor, mọi thứ khởi tạo trong setup():

Hình 2: Component QuanDashboard. setup() chạy lúc khởi tạo (thay cho constructor). Ba thứ then chốt: useService("orm") lấy cầu nối tới server để gọi ORM; useState({...}) tạo trạng thái phản ứng — gán lại state.tong là giao diện tự vẽ lại; onWillStart chạy trước lần render đầu, nơi ta await orm.searchRead(...) để nạp số liệu. Dòng cuối registry.category("actions").add(...) đăng ký component làm một client action.
Kết quả: dashboard chạy thật trong trình duyệt
Mở menu Bảng điều khiển, OWL dựng component và điền số liệu lấy từ server:

Hình 3: Dashboard do OWL tự vẽ, số liệu thật lấy qua ORM: 4 thẻ, tổng 385 điểm (150+150+85+0), 1 thẻ hạng Vàng. Đây không phải view list/form có sẵn — mỗi pixel do component của ta điều khiển. Đường đi: onWillStart gọi searchRead → gán vào state → template đọc state.tong/state.diem/state.vang và render ba thẻ. Chụp bằng trình duyệt thật, không có lỗi Console nào.
Vài lưu ý sống còn khi làm OWL
- Lỗi OWL chỉ hiện ở TRÌNH DUYỆT. Server không báo gì — một template sai
t-hay một biến undefined chỉ nổ ở Console. Luôn mở DevTools (hoặc bắtpageerrorbằng công cụ tự động) khi component không hiện. - Nạp asset qua bundle.
.jsvà.xmlphải khai trongweb.assets_backendcủa manifest; thiếu là component không tồn tại với trình duyệt. - Sau
-uphải hard refresh (Ctrl+Shift+R). Trình duyệt cache asset cũ; sửa JS mà không refresh mạnh là bạn đang xem bản cũ và tưởng code không ăn. useStatemới phản ứng. Gán vào một thuộc tính thường (this.tong = 5) không làm UI vẽ lại; phải làthis.state.tongvớistatetạo từuseState.
Ba ý mang về
- Component OWL gồm ba mảnh: lớp JS (
class extends Component,static template,setup), template QWeb-OWL (.xml), và đăng ký vàoregistry— cả ba khai trong bundleweb.assets_backend. setup()thayconstructor: dùnguseService("orm")để gọi server,useState({...})cho trạng thái phản ứng,onWillStartđể nạp dữ liệu trước lần render đầu.- Lỗi OWL chỉ ở trình duyệt — mở Console; và sau mỗi lần
-uphải hard refresh để lấy asset mới.
Component đã hiện ba con số. Nhưng chính xác t-esc="state.tong" làm gì, và khác t-out chỗ nào? Phần sau đào vào template OWL: t-esc, t-out, t-if — cách đổ dữ liệu vào HTML an toàn và điều khiển hiển thị.