OWL
data_display
Multi Selection Buttons
Odoo 19 OWL component — Multi Selection Buttons (views)
Live preview
Interactive
Source excerpt
web/static/src/views/view_components/multi_selection_buttons.js
import { Component, onWillRender, toRaw, useEffect, useRef, useState } from "@odoo/owl";
import { browser } from "@web/core/browser/browser";
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
import { useHotkey } from "@web/core/hotkeys/hotkey_hook";
import { Time } from "@web/core/l10n/time";
import { _t } from "@web/core/l10n/translation";
import { usePopover } from "@web/core/popover/popover_hook";
import { useService } from "@web/core/utils/hooks";
import { parseXML } from "@web/core/utils/xml";
import { extractFieldsFromArchInfo } from "@web/model/relational_model/utils";
import { CallbackRecorder, useSetupAction } from "@web/search/action_hook";
import { FormArchParser } from "@web/views/form/form_arch_parser";
import { MultiCreatePopover } from "./multi_create_popover";
export class MultiSelectionButtons extends Component {
static template = "web.MultiSelectionButtons";
static props = {
reactive: {
type: Object,
shape: {
onAdd: Function,
onCancel: Function,
onDelete: Function,
nbSelected: Number,
multiCreateView: String,
resModel: String,
context: Object,
showMultiCreateTimeRange: Boolean,
visible: Boolean,
multiCreateValues: { type: Object, optional: true },
},
},
};
static components = {
Popover: MultiCreatePopover,
};
setup() {
this.viewService = useService("view");
this.dialogService = useService("dialog");
this.state = useState({ isReady: false });
onWillRender(() => {
if (this.props.reactive.visible && !this.state.isReady) {
this.loadMultiCreateView().then(() => {
this.state.isReady = true;
});
}
});
this.multiCreateValues = this.props.reactive.multiCreateValues;
this.callbackRecorder = new CallbackRecorder();
useSetupAction({
getLocalState: () => {
const multiCreateData = this.getMultiCreateDataFromPopover();
if (multiCreateData) {
this.storeMultiCreateData(multiCreateData);
}
return { multiCreateValues: this.multiCreateValues };
},
});
this.multiCreatePopover = usePopover(this.constructor.components.Popover, {
onClose: () => {
const multiCreateData = this.getMultiCreateDataFromPopover();
if (multiCreateData) {
this.storeMultiCreateData(multiCreateData);
}
},
});
this.addButtonRef = useRef("addButton");
const rootRef = useRef("root");
useEffect(
(el) => {
if (!el) {
return;
}
// @ts-ignore
const { width: parentWidth } = el.parentElement.getBoundingClientRect();
const { width } = el.getBoundingClientRect();
const left = Math.floor((parentWidth - width) / 2);
el.style.setProperty("left", `${left}px`);
},
() => [rootRef.el]
);
useHotkey("escape", () => {
if (this.props.reactive.visible) {
this.props.reactive.onCancel();
}
});
}
getMultiCreateDataFromPopover() {
const fn = this.callbackRecorder.callbacks[0];
return fn?.() || null;
}
storeMultiCreateData(multiCreateData) {
this.storeTimeRange(multiCreateData.timeRange);
this.multiCreateValues = this.computeValues(multiCreateData.record);
}
async loadMultiCreateView() {
// todo: accept variable context,... ?
const { context, resModel, multiCreateView } = this.props.reactive;
const { fields, relatedModels, views } = await this.viewService.loadViews({
context: { ...context, form_view_ref: multiCreateView },
resModel,
views: [[false, "form"]],
});
const parser = new FormArchParser();
const arch = views.form.arch;
this.multiCreateArchInfo = parser.parse(parseXML(arch), relatedModels, resModel);
const { activeFields } = extractFieldsFromArchInfo(this.multiCreateArchInfo, fields);
this.multiCreateRecordProps = { resModel, fields, activeFields, context };
}
getMultiCreatePopoverProps() {
return {
Registry / API
- Registry name
MultiSelectionButtons- Category
—- Module
web- Slug
multi-selection-buttons- Nav group
data_display