Tổng quan về khung

Giới thiệu

Khung Javascript của SoOn là một tập hợp các tính năng/khối xây dựng được cung cấp bởi tiện ích bổ sung web/ để giúp xây dựng các ứng dụng odoo chạy trên trình duyệt. Đồng thời, khung Javascript của SoOn là một ứng dụng một trang, thường được gọi là web client (có sẵn tại url /web).

Máy khách web bắt đầu như một ứng dụng được tạo bằng hệ thống tiện ích và lớp tùy chỉnh, nhưng hiện tại nó đang chuyển sang sử dụng các lớp javascript gốc và Owl làm hệ thống thành phần. Điều này giải thích tại sao cả hai hệ thống hiện đang được sử dụng trong cơ sở mã.

Từ góc độ cấp cao, máy khách web là một ứng dụng một trang: nó không cần yêu cầu toàn bộ trang từ máy chủ mỗi khi người dùng thực hiện một hành động. Thay vào đó, nó chỉ yêu cầu những gì nó cần và sau đó thay thế/cập nhật màn hình hiện tại cho phù hợp. Ngoài ra, nó còn quản lý url để giữ cho nó được đồng bộ với trạng thái hiện tại.

Khung javascript (tất cả hoặc một số phần) cũng được sử dụng trong các tình huống khác, chẳng hạn như trang web SoOn hoặc điểm bán hàng. Tài liệu tham khảo này chủ yếu tập trung vào máy khách web.

Ghi chú

Trong hệ sinh thái SoOn, các từ frontendbackend là từ đồng nghĩa tương ứng với trang web odoo (công khai) và ứng dụng khách web. Không nên nhầm lẫn thuật ngữ này với cách sử dụng phổ biến hơn của mã trình duyệt (giao diện người dùng) và máy chủ (phụ trợ).

Ghi chú

Trong tài liệu này, từ thành phần luôn đề cập đến các thành phần Owl mới và widget đề cập đến các tiện ích SoOn cũ.

Ghi chú

Tất cả sự phát triển mới nên được thực hiện trong Owl, nếu có thể!

Cấu trúc mã

Thư mục web/static/src chứa tất cả cơ sở mã web/ javascript (và css và mẫu). Dưới đây là danh sách các thư mục quan trọng nhất:

  • core/ hầu hết các tính năng cấp thấp

  • fields/ tất cả các thành phần trường

  • views/ tất cả các thành phần view javascript (form, list, ...)

  • search/ bảng điều khiển, thanh tìm kiếm, bảng tìm kiếm, ...

  • webclient/ mã cụ thể của máy khách web: thanh điều hướng, menu người dùng, dịch vụ hành động, ...

web/static/src là thư mục gốc. Mọi thứ bên trong có thể được nhập một cách đơn giản bằng cách sử dụng tiền tố @web. Ví dụ: đây là cách người ta có thể nhập hàm memoize nằm trong web/static/src/core/utils/functions:

import { memoize } from "@web/core/utils/functions";

Kiến trúc WebClient

Như đã đề cập ở trên, ứng dụng khách web là một ứng dụng cú. Đây là phiên bản đơn giản hóa một chút của mẫu của nó:

<t t-name="web.WebClient">
    <body class="o_web_client">
        <NavBar/>
        <ActionContainer/>
        <MainComponentsContainer/>
    </body>
</t>

Như chúng ta có thể thấy, về cơ bản nó là một trình bao bọc cho thanh điều hướng, hành động hiện tại và một số thành phần bổ sung. ActionContainer là thành phần bậc cao hơn sẽ hiển thị bộ điều khiển hành động hiện tại (vì vậy, một hành động của khách hàng hoặc một chế độ xem cụ thể trong trường hợp các hành động thuộc loại act_window). Quản lý các hành động là một phần quan trọng trong công việc của nó: dịch vụ hành động lưu giữ trong bộ nhớ một tập hợp tất cả các hành động đang hoạt động (được biểu thị trong đường dẫn) và điều phối từng thay đổi.

Một điều thú vị khác cần lưu ý là MainComponentsContainer: nó đơn giản là một thành phần hiển thị tất cả các thành phần đã đăng ký trong sổ đăng ký main_comComponents. Đây là cách các phần khác của hệ thống có thể mở rộng máy khách web.

Môi trường

Là một ứng dụng Owl, ứng dụng khách web SoOn xác định môi trường riêng của nó (các thành phần có thể truy cập nó bằng cách sử dụng this.env). Dưới đây là mô tả về những gì SoOn thêm vào đối tượng env được chia sẻ:

Chìa khóa

Giá trị

qweb

theo yêu cầu của Owl (chứa tất cả các mẫu)

xe buýt

main bus, dùng để điều phối một số sự kiện chung

dịch vụ

tất cả đã triển khai services (thường được truy cập bằng hook useService)

gỡ lỗi

sợi dây. Nếu không trống, máy khách web đang ở chế độ debug

_t

chức năng dịch thuật

làNhỏ

boolean. Nếu đúng, máy khách web hiện đang ở chế độ di động (chiều rộng màn hình <= 767px)

Vì vậy, ví dụ, để dịch một chuỗi trong một thành phần (lưu ý: các mẫu được dịch tự động, do đó không cần thực hiện hành động cụ thể nào trong trường hợp đó), người ta có thể thực hiện điều này:

const someString = this.env._t('some text');

Ghi chú

Việc tham chiếu đến môi trường khá mạnh mẽ vì nó cung cấp quyền truy cập vào tất cả các dịch vụ. Điều này hữu ích trong nhiều trường hợp: ví dụ: các mục menu người dùng hầu hết được xác định dưới dạng một chuỗi và một hàm lấy env làm đối số duy nhất. Điều này đủ để thể hiện mọi nhu cầu về menu của người dùng.

Khu nhà

Hầu hết máy khách web được xây dựng với một số loại trừu tượng: đăng ký, dịch vụ, thành phần và hook.

Đăng ký

Registries về cơ bản là một ánh xạ khóa/giá trị đơn giản để lưu trữ một số loại đối tượng cụ thể. Chúng là một phần quan trọng trong khả năng mở rộng của giao diện người dùng: khi một số đối tượng được đăng ký, phần còn lại của máy khách web có thể sử dụng đối tượng đó. Ví dụ: sổ đăng ký trường chứa tất cả các thành phần trường (hoặc tiện ích con) có thể được sử dụng trong dạng xem.

import { registry } from "./core/registry";

class MyFieldChar extends owl.Component {
    // some code
}

registry.category("fields").add("my_field_char", MyFieldChar);

Lưu ý rằng chúng tôi nhập sổ đăng ký chính từ @web/core/registry sau đó mở sổ đăng ký phụ fields.

Dịch vụ

Dịch vụ là những đoạn mã tồn tại lâu dài cung cấp một tính năng. Chúng có thể được nhập theo các thành phần (với useService) hoặc bởi các dịch vụ khác. Ngoài ra, họ có thể khai báo một tập hợp các phụ thuộc. Theo nghĩa đó, các dịch vụ về cơ bản là một hệ thống DI (tiêm phụ thuộc). Ví dụ: dịch vụ thông báo cung cấp cách hiển thị thông báo hoặc dịch vụ rpc là cách thích hợp để thực hiện yêu cầu tới máy chủ SoOn.

Ví dụ sau đăng ký một dịch vụ đơn giản hiển thị thông báo cứ sau 5 giây:

import { registry } from "./core/registry";

const serviceRegistry = registry.category("services");

const myService = {
    dependencies: ["notification"],
    start(env, { notification }) {
        let counter = 1;
        setInterval(() => {
            notification.add(`Tick Tock ${counter++}`);
        }, 5000);
    }
};

serviceRegistry.add("myService", myService);

Linh kiện và Hooks

Componentshooks là những ý tưởng đến từ Hệ thống thành phần Owl. Các thành phần của SoOn đơn giản là các thành phần cú, là một phần của ứng dụng khách web.

Hook là một cách để phân tích mã, ngay cả khi nó phụ thuộc vào vòng đời. Đây là một cách có thể kết hợp/chức năng để đưa một tính năng vào một thành phần. Chúng có thể được xem như một loại mixin.

function useCurrentTime() {
    const state = useState({ now: new Date() });
    const update = () => state.now = new Date();
    let timer;
    onWillStart(() => timer = setInterval(update, 1000));
    onWillUnmount(() => clearInterval(timer));
    return state;
}

Bối cảnh

Một khái niệm quan trọng trong javascript của SoOn là ngữ cảnh: nó cung cấp một cách để mã cung cấp thêm ngữ cảnh cho lệnh gọi hàm hoặc rpc, để các bộ phận khác của hệ thống có thể phản ứng chính xác với thông tin đó. Ở một khía cạnh nào đó, nó giống như một túi thông tin được truyền bá khắp nơi. Nó rất hữu ích trong một số trường hợp, chẳng hạn như cho phép máy chủ SoOn biết rằng một rpc mô hình đến từ một chế độ xem biểu mẫu cụ thể hoặc kích hoạt/vô hiệu hóa một số tính năng trong một thành phần.

Có hai ngữ cảnh khác nhau trong ứng dụng khách web SoOn: ngữ cảnh người dùngngữ cảnh hành động (vì vậy, chúng ta nên cẩn thận khi sử dụng từ ngữ cảnh: nó có thể mang nghĩa khác tùy theo tình huống).

Ghi chú

Đối tượng context có thể hữu ích trong nhiều trường hợp, nhưng bạn nên cẩn thận đừng lạm dụng nó! Nhiều vấn đề có thể được giải quyết theo cách tiêu chuẩn mà không cần sửa đổi ngữ cảnh.

Bối cảnh người dùng

ngữ cảnh người dùng là một đối tượng nhỏ chứa nhiều thông tin khác nhau liên quan đến người dùng hiện tại. Nó có sẵn thông qua dịch vụ user:

class MyComponent extends Component {
    setup() {
        const user = useService("user");
        console.log(user.context);
    }
}

Nó chứa các thông tin sau:

Tên

Kiểu

Sự miêu tả

được phép_công ty_ids

số[]

danh sách id công ty đang hoạt động cho người dùng

lang

chuỗi

mã ngôn ngữ người dùng (chẳng hạn như "en_us")

tz

chuỗi

múi giờ hiện tại của người dùng (ví dụ: "Châu Âu/Brussels")

Trong thực tế, dịch vụ orm tự động thêm bối cảnh người dùng vào từng yêu cầu của nó. Đây là lý do tại sao thường không cần thiết phải nhập trực tiếp trong hầu hết các trường hợp.

Ghi chú

Phần tử đầu tiên của allowed_company_ids là công ty chính của người dùng.

Bối cảnh hành động

ir.actions.act_windowir.actions.client hỗ trợ trường context tùy chọn. Trường này là một char đại diện cho một đối tượng. Bất cứ khi nào hành động tương ứng được tải trong máy khách web, trường ngữ cảnh này sẽ được đánh giá dưới dạng một đối tượng và được cấp cho thành phần tương ứng với hành động đó.

<field name="context">{'search_default_customer': 1}</field>

Nó có thể được sử dụng theo nhiều cách khác nhau. Ví dụ: các khung nhìn thêm ngữ cảnh hành động vào mọi yêu cầu được gửi tới máy chủ. Một cách sử dụng quan trọng khác là kích hoạt một số bộ lọc tìm kiếm theo mặc định (xem ví dụ ở trên).

Đôi khi, khi chúng tôi thực hiện các hành động mới theo cách thủ công (như vậy, theo lập trình, trong javascript), việc có thể mở rộng ngữ cảnh hành động sẽ rất hữu ích. Điều này có thể được thực hiện bằng đối số bổ sung_bối cảnh.

// in setup
let actionService = useService("action");

// in some event handler
actionService.doAction("addon_name.something", {
    additional_context:{
        default_period_id: defaultPeriodId
    }
});

Trong ví dụ này, hành động có xml_id addon_name.something sẽ được tải và ngữ cảnh của nó sẽ được mở rộng với giá trị default_ Period_id. Đây là một usecase rất quan trọng cho phép các nhà phát triển kết hợp các hành động lại với nhau bằng cách cung cấp một số thông tin cho hành động tiếp theo.

Trình thông dịch Python

Khung SoOn có trình thông dịch python nhỏ được tích hợp sẵn. Mục đích của nó là đánh giá các biểu thức python nhỏ. Điều này rất quan trọng vì các dạng xem trong SoOn có các công cụ sửa đổi được viết bằng python nhưng chúng cần được trình duyệt đánh giá.

Ví dụ:

import { evaluateExpr } from "@web/core/py_js/py";

evaluateExpr("1 + 2*{'a': 1}.get('b', 54) + v", { v: 33 }); // returns 142

Mã javascript py xuất 5 hàm:

tokenize(expr)
Đối số
  • expr (string()) -- biểu thức để token hóa

Trả về

Mã thông báo[] danh sách mã thông báo

parse(tokens)
Đối số
  • tokens (Token[]()) -- danh sách token

Trả về

AST một cấu trúc cây cú pháp trừu tượng biểu diễn biểu thức

parseExpr(expr)
Đối số
  • expr (string()) -- một chuỗi biểu thị một biểu thức python hợp lệ

Trả về

AST một cấu trúc cây cú pháp trừu tượng biểu diễn biểu thức

evaluate(ast[, context])
Đối số
  • ast (AST()) -- cấu trúc AST đại diện cho một biểu thức

  • context (Object()) -- một đối tượng cung cấp bối cảnh đánh giá bổ sung

Trả về

bất kỳ giá trị kết quả nào của biểu thức, liên quan đến ngữ cảnh

evaluateExpr(expr[, context])
Đối số
  • expr (string()) -- một chuỗi biểu thị một biểu thức python hợp lệ

  • context (Object()) -- một đối tượng cung cấp bối cảnh đánh giá bổ sung

Trả về

bất kỳ giá trị kết quả nào của biểu thức, liên quan đến ngữ cảnh

Tên miền

Nói rộng ra, các miền trong SoOn đại diện cho một tập hợp các bản ghi phù hợp với một số điều kiện cụ thể. Trong javascript, chúng thường được biểu diễn dưới dạng danh sách các điều kiện (hoặc các toán tử: |, & hoặc ! trong ký hiệu tiền tố) hoặc dưới dạng biểu thức chuỗi. Chúng không cần phải được chuẩn hóa (toán tử & được ngụ ý nếu cần thiết). Ví dụ:

// list of conditions
[]
[["a", "=", 3]]
[["a", "=", 1], ["b", "=", 2], ["c", "=", 3]]
["&", "&", ["a", "=", 1], ["b", "=", 2], ["c", "=", 3]]
["&", "!", ["a", "=", 1], "|", ["a", "=", 2], ["a", "=", 3]]

// string expressions
"[('some_file', '>', a)]"
"[('date','>=', (context_today() - datetime.timedelta(days=30)).strftime('%Y-%m-%d'))]"
"[('date', '!=', False)]"

Biểu thức chuỗi mạnh hơn biểu thức danh sách: chúng có thể chứa biểu thức python và các giá trị không được đánh giá, điều này phụ thuộc vào một số ngữ cảnh đánh giá. Tuy nhiên, việc thao tác các biểu thức chuỗi khó khăn hơn.

Vì tên miền khá quan trọng trong ứng dụng web nên SoOn cung cấp lớp Miền:

new Domain([["a", "=", 3]]).contains({ a: 3 }) // true

const domain = new Domain(["&", "&", ["a", "=", 1], ["b", "=", 2], ["c", "=", 3]]);
domain.contains({ a: 1, b: 2, c: 3 }); // true
domain.contains({ a: -1, b: 2, c: 3 }); // false

// next expression returns ["|", ("a", "=", 1), ("b", "<=", 3)]
Domain.or([[["a", "=", 1]], "[('b', '<=', 3)]"]).toString();

Đây là mô tả lớp Domain:

class Domain([descr])
Đối số
  • descr (string | any[] | Domain()) -- mô tả tên miền

Domain.contains(record)
Đối số
  • record (Object()) -- một đối tượng ghi lại

Trả về

boolean

Trả về true nếu bản ghi khớp với tất cả điều kiện do miền chỉ định

Domain.toString()
Trả về

sợi dây

Trả về mô tả chuỗi cho tên miền

Domain.toList([context])
Đối số
  • context (Object()) -- bối cảnh đánh giá

Trả về

bất kì[]

Trả về mô tả danh sách cho miền. Lưu ý rằng phương thức này lấy một đối tượng context tùy chọn sẽ được sử dụng để thay thế tất cả các biến miễn phí.

new Domain(`[('a', '>', b)]`).toList({ b:3 }); // [['a', '>', 3]]

Lớp Domain cũng cung cấp 4 phương thức tĩnh hữu ích để kết hợp các miền:

// ["&", ("a", "=", 1), ("uid", "<=", uid)]
Domain.and([[["a", "=", 1]], "[('uid', '<=', uid)]"]).toString();

// ["|", ("a", "=", 1), ("uid", "<=", uid)]
Domain.or([[["a", "=", 1]], "[('uid', '<=', uid)]"]).toString();

// ["!", ("a", "=", 1)]
Domain.not([["a", "=", 1]]).toString();

// ["&", ("a", "=", 1), ("uid", "<=", uid)]
Domain.combine([[["a", "=", 1]], "[('uid', '<=', uid)]"], "AND").toString();
static Domain.and(domains)
Tham số

domains (string[] | any[][] | Domain[]) -- một danh sách các đại diện tên miền

Trả về

Lãnh địa

Trả về một miền đại diện cho giao điểm của tất cả các miền.

static Domain.or(domains)
Tham số

domains (string[] | any[][] | Domain[]) -- một danh sách các đại diện tên miền

Trả về

Lãnh địa

Trả về một miền đại diện cho sự kết hợp của tất cả các miền.

static Domain.not(domain)
Tham số

domain (string | any[] | Domain) -- một đại diện tên miền

Trả về

Lãnh địa

Trả về một miền đại diện cho sự phủ định của đối số miền

static Domain.combine(domains, operator)
Tham số
  • domains (string[] | any[][] | Domain[]) -- một danh sách các đại diện tên miền

  • operator ('AND' or 'OR') -- Một nhà điều hành

Trả về

Lãnh địa

Trả về một miền đại diện cho giao hoặc hợp của tất cả các miền, tùy thuộc vào giá trị của đối số toán tử.

Xe buýt

Đối tượng web client environment chứa một bus sự kiện, có tên là bus. Mục đích của nó là cho phép các bộ phận khác nhau của hệ thống tự phối hợp chính xác mà không cần ghép nối chúng. env.bus là một con cú EventBus, nên được sử dụng cho các sự kiện toàn cầu được quan tâm.

// for example, in some service code:
env.bus.on("WEB_CLIENT_READY", null, doSomething);

Dưới đây là danh sách các sự kiện có thể được kích hoạt trên xe buýt này:

Tin nhắn

Khối hàng

Cò súng

ACTION_MANAGER:UI-CẬP NHẬT

chế độ cho biết phần nào của giao diện người dùng đã được cập nhật ('hiện tại', 'mới' hoặc 'toàn màn hình')

việc hiển thị hành động được yêu cầu cho người quản lý hành động đã được thực hiện

ACTION_MANAGER:CẬP NHẬT

thông tin kết xuất tiếp theo

người quản lý hành động đã tính toán xong giao diện tiếp theo

`` MENU: ỨNG DỤNG THAY ĐỔI``

không có

ứng dụng hiện tại của dịch vụ menu đã thay đổi

ROUTE_CHANGE

không có

hàm băm url đã được thay đổi

RPC: YÊU CẦU

id rpc

một yêu cầu rpc vừa mới bắt đầu

RPC:PHẢN HỒI

id rpc

yêu cầu rpc đã hoàn tất

WEB_CLIENT_READY

không có

máy khách web đã được gắn kết

`` TẬP TRUNG-XEM``

không có

chế độ xem chính nên tập trung vào chính nó

`` CLEAR-Cache``

không có

tất cả bộ nhớ đệm nội bộ phải được xóa

RÀNG-KHÔNG ĐƯỢC CAM KẾT-THAY ĐỔI

danh sách các chức năng

tất cả các chế độ xem có thay đổi không được cam kết sẽ xóa chúng và đẩy lệnh gọi lại vào danh sách

Đối tượng trình duyệt

Khung javascript cũng cung cấp một đối tượng đặc biệt trình duyệt cung cấp quyền truy cập vào nhiều API trình duyệt, như location, localStorage hoặc setTimeout. Ví dụ: đây là cách người ta có thể sử dụng hàm browser.setTimeout:

import { browser } from "@web/core/browser/browser";

// somewhere in code
browser.setTimeout(someFunction, 1000);

Nó chủ yếu thú vị cho mục đích thử nghiệm: tất cả mã sử dụng đối tượng trình duyệt có thể được kiểm tra dễ dàng bằng cách mô phỏng các chức năng liên quan trong suốt thời gian thử nghiệm.

Nó chứa nội dung sau:

addEventListener

hủyAnimationFrame

clearInterval

clearTimeout

bảng điều khiển

Ngày

tìm nạp

lịch sử

Bộ nhớ cục bộ

vị trí

điều hướng

mở

ngẫu nhiên

removeEventListener

requestAnimationFrame

lưu trữ phiên

setInterval

setTimeout

XMLHttpRequest

Chế độ kiểm tra sửa lỗi

SoOn đôi khi có thể hoạt động ở chế độ đặc biệt gọi là chế độ gỡ lỗi. Nó được sử dụng cho hai mục đích chính:

  • hiển thị thông tin/trường bổ sung cho một số màn hình cụ thể,

  • cung cấp một số công cụ bổ sung để giúp nhà phát triển gỡ lỗi giao diện SoOn.

Chế độ debug được mô tả bằng một chuỗi. Chuỗi trống có nghĩa là chế độ debug không hoạt động. Nếu không thì nó đang hoạt động. Nếu chuỗi chứa assets hoặc tests thì các chế độ phụ cụ thể tương ứng sẽ được kích hoạt (xem bên dưới). Cả hai chế độ đều có thể hoạt động cùng lúc, ví dụ như với chuỗi assets,tests.

Giá trị hiện tại của chế độ debug có thể được đọc trong environment: env.debug.

Mẹo

Để chỉ hiển thị menu, trường hoặc thành phần xem ở chế độ gỡ lỗi, bạn nên nhắm mục tiêu nhóm base.group_no_one:

<field name="fname" groups="base.group_no_one"/>

Chế độ tài sản

Chế độ phụ debug=assets rất hữu ích để gỡ lỗi mã javascript: sau khi được kích hoạt, các gói assets không còn được thu nhỏ và bản đồ nguồn cũng được tạo. Điều này giúp việc gỡ lỗi tất cả các loại mã javascript trở nên hữu ích.

Chế độ kiểm tra

Có một chế độ phụ khác tên là tests: nếu được bật, máy chủ sẽ đưa gói web.assets_tests vào trang. Gói này chủ yếu chứa các chuyến tham quan thử nghiệm (các chuyến tham quan có mục đích là thử nghiệm một tính năng chứ không phải để hiển thị điều gì đó thú vị cho người dùng). Chế độ kiểm tra sau đó rất hữu ích để có thể chạy các chuyến tham quan này.

Xem thêm