这是action系列的第三篇文章
action_service 是action的核心。
整个文件接近1500行代码,其中关键的两个函数是:
doAction,
doActionButton,
// ---------------------------------------------------------------------------
// public API
// ---------------------------------------------------------------------------
/**
* Main entry point of a 'doAction' request. Loads the action and executes it.
*
* @param {ActionRequest} actionRequest
* @param {ActionOptions} options
* @returns {Promise<number | undefined | void>}
*/
async function doAction(actionRequest, options = {}) {
const actionProm = _loadAction(actionRequest, options.additionalContext);
let action = await keepLast.add(actionProm);
action = _preprocessAction(action, options.additionalContext);
options.clearBreadcrumbs = action.target === "main" || options.clearBreadcrumbs;
switch (action.type) {
case "ir.actions.act_url":
return _executeActURLAction(action, options);
case "ir.actions.act_window":
if (action.target !== "new") {
const canProceed = await clearUncommittedChanges(env);
if (!canProceed) {
return new Promise(() => {});
}
}
return _executeActWindowAction(action, options);
case "ir.actions.act_window_close":
return _executeCloseAction({ onClose: options.onClose, onCloseInfo: action.infos });
case "ir.actions.client":
return _executeClientAction(action, options);
case "ir.actions.report":
return _executeReportAction(action, options);
case "ir.actions.server":
return _executeServerAction(action, options);
default: {
const handler = actionHandlersRegistry.get(action.type, null);
if (handler !== null) {
return handler({ env, action, options });
}
throw new Error(
`The ActionManager service can't handle actions of type ${action.type}`
);
}
}
}
注释中写的很清楚:
doAction请求的主要入口点,加载一个Action并执行它。
根据不同的action类型调用不同的函数处理。
注意最后的default:
const handler = actionHandlersRegistry.get(action.type, null);
if (handler !== null) {
return handler({ env, action, options });
}
throw new Error(
`The ActionManager service can't handle actions of type ${action.type}`
);
这里说明,odoo支持用户扩展自己的动作类型,并且将动作类型的处理函数注册到注册表中,这里就可以调用了。
有哪些场景需要扩展action的类型呢?
/**
* Executes an action on top of the current one (typically, when a button in a
* view is clicked). The button may be of type 'object' (call a given method
* of a given model) or 'action' (execute a given action). Alternatively, the
* button may have the attribute 'special', and in this case an
* 'ir.actions.act_window_close' is executed.
*
* @param {DoActionButtonParams} params
* @returns {Promise<void>}
*/
async function doActionButton(params) {
// determine the action to execute according to the params
let action;
const context = makeContext([params.context, params.buttonContext]);
if (params.special) {
action = { type: "ir.actions.act_window_close", infos: { special: true } };
} else if (params.type === "object") {
// call a Python Object method, which may return an action to execute
let args = params.resId ? [[params.resId]] : [params.resIds];
if (params.args) {
let additionalArgs;
try {
// warning: quotes and double quotes problem due to json and xml clash
// maybe we should force escaping in xml or do a better parse of the args array
additionalArgs = JSON.parse(params.args.replace(/'/g, '"'));
} catch {
browser.console.error("Could not JSON.parse arguments", params.args);
}
args = args.concat(additionalArgs);
}
const callProm = env.services.rpc("/web/dataset/call_button", {
args,
kwargs: { context },
method: params.name,
model: params.resModel,
});
action = await keepLast.add(callProm);
action =
action && typeof action === "object"
? action
: { type: "ir.actions.act_window_close" };
if (action.help) {
action.help = markup(action.help);
}
} else if (params.type === "action") {
// execute a given action, so load it first
context.active_id = params.resId || null;
context.active_ids = params.resIds;
context.active_model = params.resModel;
action = await keepLast.add(_loadAction(params.name, context));
} else {
throw new InvalidButtonParamsError("Missing type for doActionButton request");
}
// filter out context keys that are specific to the current action, because:
// - wrong default_* and search_default_* values won't give the expected result
// - wrong group_by values will fail and forbid rendering of the destination view
const currentCtx = {};
for (const key in params.context) {
if (key.match(CTX_KEY_REGEX) === null) {
currentCtx[key] = params.context[key];
}
}
const activeCtx = { active_model: params.resModel };
if (params.resId) {
activeCtx.active_id = params.resId;
activeCtx.active_ids = [params.resId];
}
action.context = makeContext([currentCtx, params.buttonContext, activeCtx, action.context]);
// in case an effect is returned from python and there is already an effect
// attribute on the button, the priority is given to the button attribute
const effect = params.effect ? evaluateExpr(params.effect) : action.effect;
const options = { onClose: params.onClose };
await doAction(action, options);
if (params.close) {
await _executeCloseAction();
}
if (effect) {
env.services.effect.add(effect);
}
}
1、当点击一个按钮的时候会调用这段代码,如果按钮有special属性,那么会执行ir.actions.act_window_close,也就是关闭当前窗口。
2、按钮有两张类型 object 和 action
3、如果是object,会执行调用rpc,调用后端代码
4、最终都是调用doAction函数执行的动作。
更具体的分析留到以后,这里只是简单的分析框架。
吐槽一下,在这个文件里发现一处有瑕疵的代码:
const DIALOG_SIZES = {
"extra-large": "xl",
large: "lg",
medium: "md",
small: "sm",
};