第六章 服务
服务的定义
OWL中的服务定义是一段小的具有某种功能特性的常驻代码。服务可以被组件或另外一个服务引用,或者被声明为一种依赖注入。例如,通知(notification)服务提供了一种通知用户的功能,而RPC服务则提供了一种和后端接口通信的通道。
下面的代码展示了如何注册一个每间隔5秒显示一个通知的代码示例:
import { registry } from "@web/core/registry";
const myService = {
dependencies: ["notification"],
start(env, { notification }) {
let counter = 1;
setInterval(() => {
notification.add(`Tick Tock ${counter++}`);
}, 5000);
}
};
registry.category("services").add("myService", myService);
WebClient在启动的时候,会自动加载所有已注册为服务(services)的服务,服务的名字即为它的注册名。
服务的结构
要声明一个服务,需要实现下面两个接口:
- dependencies: 依赖的服务列表,可选项。
- start(env,deps): 启动方法,接受两个参数。env:应用环境变量,deps:所有请求的依赖。start方法返回一个值或者一个Promise对象。
以上就是一个服务的主要定义,服务返回一个值或者一个Promise的对象。如果是后者,服务调用方会等待Promise执行完成,使用返回的结果作为服务的值。有些服务不会回传任何值,此时,服务的值会被设置成null。
- async: 服务可选参数,值可选为true或string列表
有些服务会提供一个异步的API,例如RPC服务提供了一个异步的函数,而ORM服务则提供了了一系列odoo的模型方法。这种情况下,组件在调用异步方法时可能会用到即将被销毁的服务。大多数情况下,异步的调用会被忽略,但是这样做依旧是个危险的动作,因为此时依赖的组件可能已经被销毁。而async就是用来通知服务的创建者所有来自被销毁的组件的异步请求都将被挂起。
原生服务
下面来介绍一下Odoo原生的几种服务及其使用方法。
ORM服务
ORM服务是odoo中用来与后端ORM方法通信的服务组件。
ORM服务的基本使用方法
我们来看一个最简单的ORM使用示例:
import { Component } from '@odoo/owl';
import { useService } from '@web/core/utils/hooks';
class MyComponent extends Component {
setup(){
this.orm = useService("orm");
}
getUsers(){
users = await this.orm.call("res.users","search",[]);
}
}
上面的示例演示了如何使用orm服务获取用户列表的方法。
X2Many字段操作
在ORM服务的定义中还存在另外一个关键人物:X2Many操作。X2Many操作是我们在WC中用来处理Many2many和One2many字段的得力工具。
首先我们来看一下X2Many命令字的组成:
export const x2ManyCommands = {
// (0, virtualID | false, { values })
CREATE: 0,
create(virtualID, values) {
delete values.id;
return [x2ManyCommands.CREATE, virtualID || false, values];
},
// (1, id, { values })
UPDATE: 1,
update(id, values) {
delete values.id;
return [x2ManyCommands.UPDATE, id, values];
},
// (2, id[, _])
DELETE: 2,
delete(id) {
return [x2ManyCommands.DELETE, id, false];
},
// (3, id[, _]) removes relation, but not linked record itself
UNLINK: 3,
unlink(id) {
return [x2ManyCommands.UNLINK, id, false];
},
// (4, id[, _])
LINK: 4,
link(id) {
return [x2ManyCommands.LINK, id, false];
},
// (5[, _[, _]])
CLEAR: 5,
clear() {
return [x2ManyCommands.CLEAR, false, false];
},
// (6, _, ids) replaces all linked records with provided ids
SET: 6,
set(ids) {
return [x2ManyCommands.SET, false, ids];
},
};
这与我们在后端学到的命令字吻合。下面我们看一下在WC的世界中如何使用。
CREATE
这里我们使用仓库模块中导入序列号的原生代码为例:
const move_line_vals = await this.orm.call("stock.move", "action_generate_lot_line_vals", [{
...this.props.move.context,
default_product_id: this.props.move.data.product_id[0],
default_location_dest_id: this.props.move.data.location_dest_id[0],
default_location_id: this.props.move.data.location_id[0],
default_tracking: this.props.move.data.has_tracking,
default_quantity: qtyToProcess,
},
this.props.mode,
this.nextSerial.el?.value,
count,
this.lots.el?.value,
]);
const newlines = [];
let lines = []
lines = this.props.move.data.move_line_ids;
// create records directly from values to bypass onchanges
for (const values of move_line_vals) {
newlines.push(
lines._createRecordDatapoint(values, {
mode: 'readonly',
virtualId: getId("virtual"),
manuallyAdded: false,
})
);
}
if (!this.keepLines.el.checked) {
await lines._applyCommands(lines._currentIds.map((currentId) => [
x2ManyCommands.DELETE,
currentId,
]));
}
lines.records.push(...newlines);
lines._commands.push(...newlines.map((record) => [
x2ManyCommands.CREATE,
record._virtualId,
]));
lines._currentIds.push(...newlines.map((record) => record._virtualId));
await lines._onUpdate();
this.props.close();
这段代码中,首先使用orm服务,调用了库存移动(stock.move)的action_generate_lot_line_vals方法生成了一个新的批次号行数据,然后使用_createRecordDatapoint方法将数据插入到行中,组后使用_onUpdate方法将更新后的数据应用到系统中。
UPDATE
UPDATE命令用来更新X2many字段的值,格式为:[x2manyCommands.UPDATE,recordId,values]。
async matchSupplierStock() {
const lot_ids = await this.orm.call("stock.move","action_match_supplier_stock",[this.props.record.evalContext.id
])
const move_line_ids = this.props.record.data.move_line_ids;
const values = move_line_ids._currentIds.map((_currentId)=>{
return [x2ManyCommands.UPDATE,_currentId, {'lot_id': lot_ids.pop()}]
});
move_line_ids._applyCommands(values)
await move_line_ids._onUpdate();
}
上面代码的例子即为UPDATE命令字的使用示例。