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ừ frontend và backend 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ấpfields/
tất cả các thành phần trườngviews/
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ị |
---|---|
|
theo yêu cầu của Owl (chứa tất cả các mẫu) |
|
main bus, dùng để điều phối một số sự kiện chung |
|
tất cả đã triển khai services (thường được truy cập bằng hook |
|
sợi dây. Nếu không trống, máy khách web đang ở chế độ debug |
|
chức năng dịch thuật |
|
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¶
Components và hooks 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ùng và ngữ 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ả |
---|---|---|
|
|
danh sách id công ty đang hoạt động cho người dùng |
|
|
mã ngôn ngữ người dùng (chẳng hạn như "en_us") |
|
|
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_window và ir.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ứccontext (
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 |
---|---|---|
|
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 |
|
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 |
|
không có |
hàm băm url đã được thay đổi |
|
id rpc |
một yêu cầu rpc vừa mới bắt đầu |
|
id rpc |
yêu cầu rpc đã hoàn tất |
|
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 |
|
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:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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"/>
Xem thêm
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