1. Tạo một thế giới quan xin chào

Bước đầu tiên là tạo một triển khai JavaScript với một thành phần đơn giản.

  1. Tạo các tệp gallery_view.js , gallery_controller.jsgallery_controller.xml trong static/src.

  2. Triển khai thành phần hello world đơn giản trong gallery_controller.js.

  3. Trong gallery_view.js, nhập bộ điều khiển, tạo đối tượng chế độ xem và đăng ký nó trong sổ đăng ký chế độ xem dưới tên gallery.

    Example

    Dưới đây là một ví dụ về cách xác định đối tượng xem:

    import { registry } from "@web/core/registry";
    import { MyController } from "./my_controller";
    
    export const myView = {
          type: "my_view",
          display_name: "MyView",
          icon: "oi oi-view-list",
          multiRecord: true,
          Controller: MyController,
    };
    
    registry.category("views").add("my_controller", myView);
    
  4. Thêm gallery làm một trong các loại chế độ xem trong hành động contacts.action_contacts.

  5. Đảm bảo rằng bạn có thể thấy thành phần hello world khi chuyển sang chế độ xem thư viện.

../../../_images/view_button.png ../../../_images/new_view.png

2. Sử dụng thành phần Layout

Cho đến nay, chế độ xem thư viện của chúng tôi trông không giống chế độ xem tiêu chuẩn. Hãy sử dụng thành phần Layout để có các tính năng tiêu chuẩn như các chế độ xem khác.

  1. Nhập thành phần Layout và thêm nó vào thành phần của GalleryController.

  2. Cập nhật mẫu để sử dụng Layout. Nó cần một prop display, có thể tìm thấy trong props.display.

../../../_images/layout.png

3. Phân tích vòm

Hiện tại, chế độ xem thư viện của chúng tôi không có tác dụng gì nhiều. Hãy bắt đầu bằng cách đọc thông tin có trong vòm của khung nhìn.

Quá trình phân tích cú pháp một vòm thường được thực hiện bằng ArchParser, dành riêng cho từng chế độ xem. Nó kế thừa từ một lớp XMLParser chung.

Example

Đây là một ví dụ về giao diện của ArchParser:

export class MyCustomArchParser {
    parse(xmlDoc) {
       const myAttribute = xmlDoc.getAttribute("my_attribute")
       return {
           myAttribute,
       }
    }
}
  1. Tạo lớp ArchParser trong tệp riêng của nó.

  2. Sử dụng nó để đọc thông tin image_field.

  3. Cập nhật mã xem thư viện để thêm nó vào đạo cụ mà bộ điều khiển nhận được.

Ghi chú

Làm như vậy có lẽ hơi quá mức cần thiết, vì về cơ bản chúng ta chỉ cần đọc một thuộc tính từ vòm, nhưng đó là một thiết kế được mọi chế độ xem odoo khác sử dụng, vì nó cho phép chúng ta trích xuất một số xử lý trả trước từ bộ điều khiển.

4. Tải một số dữ liệu

Bây giờ chúng ta hãy lấy một số dữ liệu thực từ máy chủ. Để làm được điều đó, chúng ta phải sử dụng webSearchRead từ dịch vụ orm.

Example

Dưới đây là ví dụ về webSearchRead để lấy bản ghi từ một mô hình:

const { length, records } = this.orm.webSearchRead(this.resModel, domain, {
   specification: {
        [this.fieldToFetch]: {},
        [this.secondFieldToFetch]: {},
    },
    context: {
        bin_size: true,
    }
})
  1. Thêm phương thức loadImages(domain) {...} vào GalleryController. Nó sẽ thực hiện lệnh gọi webSearchRead từ dịch vụ orm để tìm nạp các bản ghi tương ứng với miền và sử dụng imageField nhận được trong đạo cụ.

  2. Nếu không thêm bin_size vào ngữ cảnh của cuộc gọi, bạn sẽ nhận được trường hình ảnh được mã hóa ở base64. Đảm bảo đặt bin_size trong ngữ cảnh để nhận kích thước của trường hình ảnh. Chúng tôi sẽ hiển thị hình ảnh sau.

  3. Sửa đổi mã setup để gọi phương thức đó trong hook onWillStartonWillUpdateProps.

  4. Sửa đổi mẫu để hiển thị id và kích thước của từng hình ảnh bên trong vị trí mặc định của thành phần Layout.

Ghi chú

Mã dữ liệu tải sẽ được chuyển sang mô hình phù hợp trong bài tập tiếp theo.

../../../_images/gallery_data.png

5. Giải quyết vấn đề đồng thời

Hiện tại, mã của chúng tôi không phải là bằng chứng đồng thời. Nếu một người thay đổi tên miền hai lần, nó sẽ kích hoạt loadImages(domain) hai lần. Do đó, chúng tôi có hai yêu cầu có thể đến vào thời điểm khác nhau tùy thuộc vào các yếu tố khác nhau. Việc nhận phản hồi cho yêu cầu đầu tiên sau khi nhận được phản hồi cho yêu cầu thứ hai sẽ dẫn đến trạng thái không nhất quán.

Nguyên hàm KeepLast của SoOn giải quyết vấn đề này, nó quản lý danh sách các tác vụ và chỉ giữ cho tác vụ cuối cùng hoạt động.

  1. Nhập KeepLast từ @web/core/utils/concurrency.

  2. Khởi tạo đối tượng KeepLast trong mô hình.

  3. Thêm lệnh gọi webSearchRead vào KeepLast để chỉ lệnh gọi cuối cùng được giải quyết.

6. Sắp xếp lại mã

Chế độ xem thực tế có tổ chức hơn một chút. Điều này có thể là quá mức cần thiết trong ví dụ này nhưng nó nhằm mục đích tìm hiểu cách cấu trúc mã trong SoOn. Ngoài ra, điều này sẽ mở rộng quy mô tốt hơn với các yêu cầu thay đổi.

  1. Di chuyển tất cả mã mô hình vào lớp GalleryModel của chính nó.

  2. Di chuyển tất cả mã kết xuất trong thành phần GalleryRenderer.

  3. Nhập GalleryModelGalleryRenderer trong GalleryController để làm cho nó hoạt động.

7. Làm cho chế độ xem có thể mở rộng

Để mở rộng chế độ xem, người ta có thể nhập đối tượng chế độ xem thư viện để sửa đổi nó theo sở thích của họ. Vấn đề là hiện tại, không thể xác định mô hình hoặc trình kết xuất tùy chỉnh vì nó được mã hóa cứng trong bộ điều khiển.

  1. Nhập GalleryModelGalleryRenderer trong tệp xem thư viện.

  2. Thêm khóa ModelRenderer vào đối tượng chế độ xem thư viện và gán chúng cho GalleryModelGalleryRenderer. Truyền ModelRenderer làm đạo cụ cho bộ điều khiển.

  3. Loại bỏ quá trình nhập mã hóa cứng trong bộ điều khiển và lấy chúng từ đạo cụ.

  4. Sử dụng t-comComponent để có thành phần phụ động.

Ghi chú

Đây là cách giờ đây ai đó có thể mở rộng chế độ xem thư viện bằng cách sửa đổi trình kết xuất:

/** @odoo-module */

import { registry } from '@web/core/registry';
import { galleryView } from '@awesome_gallery/gallery_view';
import { GalleryRenderer } from '@awesome_gallery/gallery_renderer';

export class MyExtendedGalleryRenderer extends GalleryRenderer {
   static template = "my_module.MyExtendedGalleryRenderer";
   setup() {
      super.setup();
      console.log("my gallery renderer extension");
   }
}

registry.category("views").add("my_gallery", {
   ...galleryView,
   Renderer: MyExtendedGalleryRenderer,
});

8. Hiển thị hình ảnh

Cập nhật trình kết xuất để hiển thị hình ảnh theo cách đẹp mắt nếu trường này được đặt. Nếu image_field trống, thay vào đó hãy hiển thị một hộp trống.

Mẹo

Có một bộ điều khiển cho phép lấy hình ảnh từ một bản ghi. Bạn có thể sử dụng đoạn mã này để xây dựng liên kết:

import { url } from "@web/core/utils/urls";
const url = url("/web/image", {
   model: resModel,
   id: image_id,
   field: imageField,
});
../../../_images/tshirt_images.png

9. Chuyển sang chế độ xem biểu mẫu khi nhấp chuột

Cập nhật trình kết xuất để phản ứng với một lần nhấp vào hình ảnh và chuyển sang chế độ xem biểu mẫu. Bạn có thể sử dụng chức năng switchView từ dịch vụ hành động.

10. Thêm chú giải công cụ tùy chọn

Sẽ rất hữu ích nếu có thêm một số thông tin về việc di chuột.

  1. Cập nhật mã để cho phép một thuộc tính bổ sung tùy chọn trên vòm:

    <gallery image_field="some_field" tooltip_field="some_other_field"/>
    
  2. Khi di chuột, hiển thị nội dung của trường chú giải công cụ. Nó sẽ hoạt động nếu trường là trường char, trường số hoặc trường many2one. Để đặt chú giải công cụ vào phần tử html, bạn có thể đặt chuỗi vào thuộc tính data-tooltip của phần tử.

  3. Cập nhật khung xem thư viện khách hàng để thêm khách hàng làm trường chú giải công cụ.

../../../_images/image_tooltip.png

Xem thêm

dụ: cách sử dụng t-att-data-tooltip <https://github.com/odoo/odoo/blob/145fe958c212ddef9fab56a232c8b2d3db635c8e/addons/survey/static/src/views/widgets/survey_question_trigger/survey_question_trigger.xml#L8> _

11. Thêm phân trang

Hãy thêm một máy nhắn tin trên bảng điều khiển và quản lý tất cả phân trang như trong chế độ xem SoOn thông thường.

../../../_images/pagination.png

12. Xác thực lượt xem

Chúng tôi có một cái nhìn tốt đẹp và hữu ích cho đến nay. Nhưng trong thực tế, chúng tôi có thể gặp vấn đề với việc người dùng mã hóa không chính xác arch của chế độ xem Thư viện của họ: nó hiện chỉ là một đoạn XML không có cấu trúc.

Hãy để chúng tôi thêm một số xác nhận! Trong SoOn, tài liệu XML có thể được mô tả bằng tệp RN (Relax NG file), sau đó được xác thực.

  1. Thêm tệp RNG mô tả ngữ pháp hiện tại:

    • Thuộc tính bắt buộc image_field.

    • Thuộc tính tùy chọn: tooltip_field.

  2. Thêm một số mã để đảm bảo tất cả các chế độ xem đều được xác thực đối với tệp RNG này.

  3. Trong khi thực hiện việc đó, hãy đảm bảo rằng image_fieldtooltip_field là các trường từ mô hình hiện tại.

Vì việc xác thực tệp RNG không hề đơn giản nên đây là đoạn trích để trợ giúp:

# -*- coding: utf-8 -*-
import logging
import os

from lxml import etree

from odoo.loglevels import ustr
from odoo.tools import misc, view_validation

_logger = logging.getLogger(__name__)

_viewname_validator = None

@view_validation.validate('viewname')
def schema_viewname(arch, **kwargs):
      """ Check the gallery view against its schema

      :type arch: etree._Element
      """
      global _viewname_validator

      if _viewname_validator is None:
         with misc.file_open(os.path.join('modulename', 'rng', 'viewname.rng')) as f:
            _viewname_validator = etree.RelaxNG(etree.parse(f))

      if _viewname_validator.validate(arch):
         return True

      for error in _viewname_validator.error_log:
         _logger.error(ustr(error))
      return False

13. Tải hình ảnh lên

Chế độ xem thư viện của chúng tôi không cho phép người dùng tải hình ảnh lên. Hãy để chúng tôi thực hiện điều đó.

  1. Thêm nút trên mỗi hình ảnh bằng cách sử dụng thành phần FileUploader.

  2. Thành phần FileUploader chấp nhận props onUploaded, được gọi khi người dùng tải hình ảnh lên. Đảm bảo gọi webSave từ dịch vụ orm để tải lên hình ảnh mới.

  3. Bạn có thể nhận thấy rằng hình ảnh đã được tải lên nhưng nó không được trình duyệt hiển thị lại. Điều này là do liên kết hình ảnh không thay đổi nên trình duyệt không tìm nạp lại chúng. Bao gồm write_date từ bản ghi vào url hình ảnh.

  4. Đảm bảo rằng việc nhấp vào nút tải lên không kích hoạt switchView.

../../../_images/upload_image.png

14. Mẫu chú giải công cụ nâng cao

Hiện tại, chúng tôi chỉ có thể chỉ định trường chú giải công cụ. Nhưng nếu chúng ta muốn cho phép viết một mẫu cụ thể cho nó thì sao?

Example

Đây là ví dụ về chế độ xem vòm thư viện sẽ hoạt động sau bài tập này.

<record id="contacts_gallery_view" model="ir.ui.view">
   <field name="name">awesome_gallery.orders.gallery</field>
   <field name="model">res.partner</field>
   <field name="arch" type="xml">
      <gallery image_field="image_1920" tooltip_field="name">
         <field name="email"/> <!-- Specify to the model that email should be fetched -->
         <field name="name"/>  <!-- Specify to the model that name should be fetched -->
         <tooltip-template> <!-- Specify the owl template for the tooltip -->
            <p class="m-0">name: <field name="name"/></p> <!-- field is compiled into a t-esc-->
            <p class="m-0">e-mail: <field name="email"/></p>
         </tooltip-template>
      </gallery>
   </field>
</record>
  1. Thay thế chế độ xem vòm thư viện res.partner trong awesome_gallery/views/views.xml bằng vòm trong ví dụ trên. Đừng lo lắng nếu nó không vượt qua được quá trình xác thực rng.

  2. Sửa đổi trình xác thực thư viện rng để chấp nhận cấu trúc vòm mới.

    Mẹo

    Bạn có thể sử dụng đoạn mã rng này để xác thực thẻ mẫu chú giải công cụ

    <rng:define name="tooltip-template">
       <rng:element name="tooltip-template">
             <rng:zeroOrMore>
                <rng:text/>
                <rng:ref name="any"/>
             </rng:zeroOrMore>
       </rng:element>
    </rng:define>
    
    <rng:define name="any">
       <rng:element>
             <rng:anyName/>
             <rng:zeroOrMore>
                <rng:choice>
                   <rng:attribute>
                         <rng:anyName/>
                   </rng:attribute>
                   <rng:text/>
                   <rng:ref name="any"/>
                </rng:choice>
             </rng:zeroOrMore>
       </rng:element>
    </rng:define>
    
  3. Trình phân tích cú pháp vòm sẽ phân tích các trường và mẫu chú giải công cụ. Nhập visitXML từ @web/core/utils/xml và sử dụng nó để phân tích tên trường và mẫu chú giải công cụ.

  4. Đảm bảo rằng mô hình gọi webSearchRead bằng cách đưa tên trường được phân tích cú pháp vào đặc tả.

  5. Trình kết xuất (hoặc bất kỳ thành phần phụ nào bạn đã tạo cho nó) sẽ nhận được mẫu chú giải công cụ được phân tích cú pháp. Thao tác với mẫu này để thay thế phần tử <field> thành phần tử <t t-esc="x">.

    Mẹo

    Mẫu là một đối tượng Element nên nó có thể được thao tác giống như một phần tử HTML.

  6. Đăng ký mẫu vào Owl nhờ hàm xml từ @odoo/owl.

  7. Sử dụng hook useTooltip từ @web/core/tooltip/tooltip_hook để hiển thị chú giải công cụ. Móc này lấy làm đối số là mẫu Owl và biến mà mẫu đó cần.

../../../_images/advanced_tooltip.png