Component đã hiển thị danh sách thẻ. Giờ cần nó phản ứng: người dùng bấm một thẻ, thẻ đó được chọn và tô sáng. Trong OWL, bạn gắn sự kiện bằng chỉ thị t-on — t-on-click, t-on-input, t-on-submit... Nghe quen như HTML, nhưng có một chỗ khác khiến người mới vấp: khi lặp danh sách, làm sao handler biết thẻ nào vừa được bấm?

Gọi thẳng, hay truyền tham số

Có hai cách viết t-on-click, và chọn đúng phụ thuộc bạn có cần truyền tham số hay không:

Ảnh chụp đoạn mã nền tối ba phần. Phần một method chonThe nhận card và ev chú thích nhận thêm ev là sự kiện DOM, gán this state chon bằng card chú thích đổi state UI vẽ lại, chú thích ev target ev preventDefault ev stopPropagation; và button t-on-click chonThe chú thích gọi method trực tiếp handler nhận ev. Phần hai trong t-foreach div t-foreach state cards t-as card t-key card id, t-att-class state chon và state chon id bằng card id thì border-primary, t-on-click mở ngoặc ev mũi tên this chonThe card ev chú thích arrow truyền đúng card của dòng, bên trong div t-esc card name. Phần ba input t-on-input ev mũi tên this timKiem ev target value, form t-on-submit ev mũi tên ev preventDefault này this luu, chú thích OWL KHÔNG có chấm stop chấm prevent như Vue nên gọi ev stopPropagation preventDefault tay

Hình 1: Gọi thẳng t-on-click="chonThe" — OWL truyền vào handler một tham số duy nhất: ev (đối tượng sự kiện DOM). Truyền tham số thì bọc trong arrow function: t-on-click="(ev) => this.chonThe(card, ev)" — nhờ đó handler biết chính xác card nào. Và OWL không có modifier .stop/.prevent như Vue: muốn chặn thì gọi ev.stopPropagation() / ev.preventDefault() bằng tay trong handler.

Đo: bấm là chọn, và tô sáng

Trước khi bấm, không có thẻ nào được chọn:

Ảnh chụp giao diện dashboard OWL mục Bấm một thẻ để chọn t-on-click, bốn thẻ VIP-0001 VIP-0003 VIP-0002 VIP-0004 với số điểm, không thẻ nào có viền nổi bật, dưới cùng dòng chữ xám Chưa chọn thẻ nào hãy bấm một thẻ

Hình 2: Trạng thái ban đầu — state.chon là null, nên không thẻ nào được tô và dòng dưới ghi "Chưa chọn thẻ nào".

Bấm vào thẻ VIP-0003, handler chonThe(card) chạy với đúng card của thẻ đó:

Ảnh chụp cùng giao diện sau khi bấm thẻ VIP-0003, thẻ VIP-0003 có viền tím dày nổi bật còn ba thẻ kia bình thường, dưới cùng dòng chữ Đang chọn VIP-0003 in đậm 150 điểm

Hình 3: Sau khi bấm — thẻ VIP-0003 có viền tím dày (nhờ t-att-class so state.chon.id === card.id), và dòng dưới đổi thành "Đang chọn: VIP-0003 (150 điểm)". Handler nhận đúng card của thẻ được bấm nhờ arrow function, gán vào state.chon, và vì chon nằm trong useState nên giao diện tự vẽ lại. Toàn bộ vòng: sự kiện → handler → đổi state → render. Chụp bằng trình duyệt thật, không lỗi Console.

Vài lưu ý về sự kiện trong OWL

  • Gọi thẳng khi không cần tham số (t-on-click="luu"), arrow khi cần (t-on-click="() => this.xoa(card)"). Đừng viết t-on-click="this.xoa(card)" — nó gọi ngay lúc render, không phải khi bấm.
  • Không có .stop/.prevent/.self. Đây là khác biệt lớn với Vue: chặn lan truyền hay hành vi mặc định phải gọi phương thức trên ev trong handler.
  • Handler luôn nhận ev cuối cùng khi gọi thẳng; với arrow, bạn tự quyết truyền gì. ev.target là element bị bấm, ev.currentTarget là element gắn t-on.
  • Sự kiện + state = tương tác. Handler nên đổi state (this.state.x = ...) để UI phản ứng, không nên thao tác DOM trực tiếp — để OWL lo phần vẽ.

Ba ý mang về

  1. t-on-click="method" gọi thẳng (handler nhận ev); t-on-click="() => this.method(arg)" dùng arrow để truyền tham số — cần thiết khi lặp t-foreach để biết item nào.
  2. OWL không có modifier .stop/.prevent như Vue — gọi ev.stopPropagation() / ev.preventDefault() bằng tay trong handler.
  3. Vòng tương tác chuẩn: sự kiện → handler → đổi state (trong useState) → OWL tự render lại; handler đừng thao tác DOM trực tiếp.

Handler của ta gọi this.orm.searchRead để lấy dữ liệu — cái orm đó từ đâu ra? Nó là một service của Odoo. Phần sau đi vào useService — gọi dịch vụ Odoo từ component: orm, notification, action, dialog... và cách chúng nối component với phần còn lại của hệ thống.