Một chỗ trong giao diện không hiện đúng, hoặc hiện rồi mà bấm không phản ứng. Bạn console.log khắp nơi trong code, build lại, tải lại trang, mò từng bước. Có cách nhanh hơn nhiều: OWL — engine giao diện của Odoo — để lộ toàn bộ cây component đang chạy ra cho bạn xem thẳng trong console trình duyệt. Không cần cài gì, không cần sửa code. Bài này chỉ cách nhìn vào cây đó, đọc state/props của bất kỳ component nào, và giải thích một chi tiết kiến trúc lộ ra khi soi: vì sao mỗi menu trên thanh điều hướng được bọc trong một ErrorHandler.
Cửa sau: window.OWL_DEVTOOLS
Khi OWL khởi động, nó gắn một hook toàn cục vào window. Đây là dòng thật trong lõi owl.js của Odoo 19:
window.__OWL_DEVTOOLS__ || (window.__OWL_DEVTOOLS__ = { apps, Fiber, RootFiber, toRaw, reactive });
Chính cái object này là thứ tiện ích Owl Devtools (extension trình duyệt) đọc để vẽ ra bảng component. Nhưng bạn không cần extension — mở console là dùng được ngay. apps là một Set chứa mọi "app" OWL trên trang; backend Odoo chỉ có một, và component gốc của nó là WebClient.

Hình 1: Vài câu lệnh mở "cửa sau" của OWL. __OWL_DEVTOOLS__.apps cho bạn app gốc; từ app.root (một ComponentNode) đi xuống theo .children là duyệt được cả cây. Mỗi node có .component (instance thật), .props (nhận từ cha) và .parent.
this.owl: mỗi component mang node của chính nó
Điểm mấu chốt để hiểu cách OWL tự soi: mỗi instance component đều có một thuộc tính this.__owl__ trỏ tới ComponentNode của nó. Lõi gán đúng một dòng:
this.__owl__ = node;
ComponentNode là "hồ sơ" của component trong cây: nó giữ component (instance), props, app, parent, và children. Nắm được một node là bạn đi được lên (.parent) hay xuống (.children) cả cây, và đọc được mọi thứ component đang giữ.
Đọc cả cây từ gốc
Chạy hàm duyệt đệ quy ở Hình 1 trên backend Odoo, đây là cây thật in ra:

Hình 2: Cây component thật của backend Odoo. Gốc là WebClient, dưới nó là NavBar, rồi các Dropdown. Để ý bốn khối ErrorHandler bọc lần lượt ActivityMenu, CallMenu, MessagingMenu, SwitchCompanyMenu — mục sau giải thích vì sao.
Chỉ nhìn cây này bạn đã hiểu cấu trúc giao diện Odoo mà không cần đọc một dòng template nào: menu trên cùng là NavBar, các biểu tượng góc phải (chuông thông báo, tin nhắn, chuyển công ty) là những component systray riêng.
Xem state và props của từng component
Có node rồi, đọc trạng thái sống của nó. node.component.state là state phản ứng (reactive), node.props là props cha truyền vào. Đây là dữ liệu thật lấy từ ba component đầu cây:

Hình 3: state/props thật. WebClient giữ fullscreen; NavBar giữ hai cờ đóng/mở menu; Dropdown nhận prop slots và giữ isOpen. Vì state là reactive, sửa thẳng nó trong console (ví dụ node.component.state.isOpen = true) sẽ khiến OWL vẽ lại component đó — cách nhanh để thử một trạng thái mà không cần thao tác chuột.
Đây là điểm khiến việc debug nhanh hẳn: thay vì đoán "state lúc này là gì", bạn đọc thẳng nó; thay vì tìm cách tái hiện một trạng thái, bạn gán thẳng vào state reactive và xem giao diện phản ứng.
Vì sao mỗi menu bọc trong ErrorHandler
Quay lại bốn khối ErrorHandler ở Hình 2. ErrorHandler là hiện thân của error boundary trong OWL: một component cha bắt lỗi render/hook của con qua hook onError, để lỗi của con không kéo sập cả cây phía trên. Odoo bọc mỗi menu systray trong một ErrorHandler riêng vì lý do rất thực dụng: các menu này do nhiều module khác nhau đóng góp (chat, hoạt động, chuyển công ty...). Nếu MessagingMenu của module chat gặp lỗi mà không có lưới, nó sẽ làm hỏng luôn cả NavBar — người dùng mất sạch thanh điều hướng chỉ vì một biểu tượng lỗi. Có ErrorHandler bao quanh, menu lỗi tự gục một mình, phần còn lại vẫn chạy.
Nhìn cây component, bạn thấy quyết định kiến trúc đó hiện ra thành cấu trúc — điều mà đọc code rời rạc khó nhận ra.
Vài mẹo debug thực dụng
- Bật
?debug=1trên URL để vào chế độ nhà phát triển: OWL kiểm tra props chặt hơn và log nhiều hơn, dễ bắt lỗi kiểu "truyền thiếu prop". - Chọn phần tử trong tab Elements rồi dùng
$0trong Console — nhưng nhớ__owl__nằm trên instance component, không phải phần tử DOM; từ$0hãy đi ngược lên quaapp.roothoặc dùng extension Owl Devtools để ánh xạ DOM ↔ component. __OWL_DEVTOOLS__.toRawgỡ lớp proxy reactive khỏi một state để in ra giá trị "trần", dễ đọc hơn khi state lồng nhau nhiều tầng.- Đọc
node.parentkhi một component nhận sai prop: đi lên cha xem chính xác nó truyền gì xuống.
Ba ý mang về
- OWL để lộ cả cây component qua
window.__OWL_DEVTOOLS__({ apps, Fiber, RootFiber, toRaw, reactive }) và quathis.__owl__trên mỗi instance — không cần cài gì, mở console là soi được từ gốcWebClientxuống. - Mỗi
ComponentNodecho bạncomponent,state,props,parent,children; state là reactive nên đọc thẳng để debug, gán thẳng để thử trạng thái và xem giao diện vẽ lại. - Cây component tiết lộ kiến trúc — bốn
ErrorHandlerbọc bốn menu systray chính là các error boundary giữ cho một menu lỗi không kéo sập cảNavBar.
Soi được front-end rồi, phần sau ta quay xuống tầng server nhưng vẫn phía "web": cách Odoo nhận và trả lời một request HTTP. Phần sau mổ xẻ http.Controller và @route — cách khai một URL, các kiểu type, và request đi qua đâu trước khi tới hàm của bạn.