Ở bài website ta viết t-call="website.layout" để mượn cả header/footer của site. Đó chính là t-call — công cụ tái sử dụng template của QWeb. Thay vì chép đi chép lại một khối HTML, bạn viết nó một lần thành template riêng rồi gọi ở mọi nơi cần. Bài này mổ t-call kỹ hơn, gồm cả cái khe kỳ lạ t-out="0" mà website.layout dùng để "bọc" nội dung của bạn.

t-call gọi một template khác vào chỗ đó

<t t-call="module.template"/> chèn (include) nội dung của template được nêu vào đúng vị trí đó. Điểm mấu chốt: template được gọi thấy cùng context với chỗ gọi — mọi biến đang có (kể cả biến vòng lặp) đều dùng được bên trong nó. Nhờ vậy bạn tách một "mảnh dùng chung" ra template riêng, rồi gọi lại với dữ liệu khác nhau mỗi lần.

Mình dựng ba template minh hoạ đủ ba kiểu dùng:

Ảnh chụp ba đoạn mã QWeb nền tối. Đoạn một là template id the_card vẽ một div card chứa h5 t-esc the chấm name và một div t-esc the chấm diem kèm chữ đ, chú thích template con dùng lại đọc biến the. Đoạn hai là template id khung_vien một div class border chứa t-out bằng 0 kèm chú thích khe 0 nghĩa là chèn body của lời gọi. Đoạn ba là trang dùng t-call website layout chứa h1 cốc cà phê Thẻ thành viên, bên trong t-call quan_ca_phe khung_vien và nội dung con là t-foreach cards t-as the t-key the id gọi t-call quan_ca_phe the_card, chú thích nội dung con rơi vào khe 0 và the_card thấy biến the

Hình 1: Ba template. (1) the_card — mảnh dùng lại, vẽ một thẻ, đọc biến the. (2) khung_vien — một cái khung có khe: t-out="0" là chỗ nội dung con của lời gọi sẽ rơi vào. (3) trang — gọi website.layout, rồi bên trong gọi khung_vien (bọc), và trong vòng t-foreach gọi the_card cho mỗi thẻ. the_card thấy biến the vì template được gọi dùng chung context với chỗ gọi.

Cái khe số 0

Đây là phần hay bị bối rối. Khi bạn viết t-call có nội dung con:

<t t-call="quan_ca_phe.khung_vien">
    ...nội dung này...
</t>

thì "nội dung này" không mất đi — nó được gói lại và truyền vào template khung_vien dưới cái tên đặc biệt 0. Trong khung_vien, <t t-out="0"/> chính là chỗ "đổ" nội dung đó ra. Đó là cách một template bọc thứ khác: website.layout cũng làm y hệt — nội dung trang của bạn rơi vào khe 0 của nó, giữa header và footer.

Nói gọn: t-call không có body → chỉ include; t-call có body → include và truyền body vào khe 0 để template kia đặt vào đâu tuỳ ý.

Kết quả thật

Trang /the-tcall render ra: một khung viền nâu (do khung_vien bọc qua khe 0), bên trong là bốn thẻ, mỗi thẻ do the_card dựng:

Ảnh chụp thật trang web the-tcall trên Odoo 19, tiêu đề cốc cà phê Thẻ thành viên dựng bằng t-call, bên dưới là một khung viền màu nâu bo góc bọc bốn thẻ card nằm ngang, thẻ VIP-0003 nhãn Vàng 150 đ, thẻ VIP-0001 nhãn Bạc 150 đ, thẻ VIP-0002 nhãn Bạc 85 đ, thẻ VIP-0004 nhãn Bạc 0 đ, mỗi thẻ cùng một kiểu dáng vì cùng dựng từ template the_card

Hình 2: Kết quả thật. Khung viền nâu là khung_vien — nội dung (bốn thẻ) được truyền vào qua khe 0. Bốn thẻ đều cùng kiểu dáng vì cùng gọi the_card; mình chỉ viết template thẻ một lần. Vòng t-foreach đặt biến the cho mỗi vòng, the_card đọc đúng the đó — VIP-0003 Vàng 150, VIP-0001 Bạc 150, VIP-0002 85, VIP-0004 0.

Muốn đổi kiểu thẻ (thêm icon, đổi bố cục)? Sửa một template the_card, cả bốn (và mọi trang khác gọi nó) đổi theo. Đó là giá trị của việc tách ra.

Vài lưu ý

  • Template con dùng chung context — tiện nhưng cũng dễ vô tình phụ thuộc biến của nơi gọi. Muốn rõ ràng, đặt biến trước bằng t-set (hoặc truyền vào khe 0) thay vì ngầm dựa vào tên biến trùng.
  • t-call khác t-call-assets. Cái sau nạp một bundle asset (JS/CSS) vào trang, không phải chèn template — đừng lẫn.
  • t-call có ở cả ba loại QWeb (OWL, report, website). Cùng ý tưởng tái sử dụng, chỉ khác nơi chạy.
  • t-out="0" mới là cách nhận body; t-out="0" render nội dung đã được QWeb dựng sẵn (markup), khác t-esc một chuỗi thường.

Ba ý mang về

  1. t-call="module.template" chèn một template vào chỗ đó và cho nó dùng chung context — tách một mảnh HTML dùng nhiều lần (một card, một khối macro) ra template riêng rồi gọi lại khắp nơi.
  2. Nội dung con của t-call rơi vào khe đặc biệt 0; template được gọi đặt nó ra bằng t-out="0". Đây là cách một template bọc thứ khác — chính website.layout hoạt động như vậy.
  3. Sửa một template dùng chung, mọi nơi gọi nó đổi theo. Đó là lý do tách ra: một nguồn sự thật cho phần lặp lại, thay vì chép HTML rồi sửa sót.

Trong the_card ta viết t-esc="the.name" — đọc field thô. Nhưng Odoo còn một directive hiểu kiểu của field và tự định dạng: phần sau nói về t-field — hiển thị dữ liệu bản ghi đúng định dạng, có thể sửa tại chỗ trên website, khác t-esc/t-out ở đâu.