OWL
data_display
List Controller
Odoo 19 OWL component — List Controller (views)
Live preview
Interactive
Source excerpt
web/static/src/views/list/list_controller.js
import { _t } from "@web/core/l10n/translation";
import { evaluateExpr, evaluateBooleanExpr } from "@web/core/py_js/py";
import { user } from "@web/core/user";
import { unique } from "@web/core/utils/arrays";
import { useService } from "@web/core/utils/hooks";
import { omit } from "@web/core/utils/objects";
import { useSetupAction } from "@web/search/action_hook";
import { ActionMenus, STATIC_ACTIONS_GROUP_NUMBER } from "@web/search/action_menus/action_menus";
import { Layout } from "@web/search/layout";
import { usePager } from "@web/search/pager_hook";
import { useModelWithSampleData } from "@web/model/model";
import { DynamicRecordList } from "@web/model/relational_model/dynamic_record_list";
import { extractFieldsFromArchInfo } from "@web/model/relational_model/utils";
import { standardViewProps } from "@web/views/standard_view_props";
import { MultiRecordViewButton } from "@web/views/view_button/multi_record_view_button";
import { ViewButton } from "@web/views/view_button/view_button";
import { executeButtonCallback, useViewButtons } from "@web/views/view_button/view_button_hook";
import { ListConfirmationDialog } from "./list_confirmation_dialog";
import { SearchBar } from "@web/search/search_bar/search_bar";
import { useSearchBarToggler } from "@web/search/search_bar/search_bar_toggler";
import { session } from "@web/session";
import { ListCogMenu } from "./list_cog_menu";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { SelectionBox } from "@web/views/view_components/selection_box";
import { useExportRecords, useDeleteRecords } from "@web/views/view_hook";
import {
Component,
onWillPatch,
onWillRender,
onWillStart,
useEffect,
useRef,
useState,
useSubEnv,
} from "@odoo/owl";
// -----------------------------------------------------------------------------
export class ListController extends Component {
static template = `web.ListView`;
static components = {
ActionMenus,
Layout,
ViewButton,
MultiRecordViewButton,
SearchBar,
CogMenu: ListCogMenu,
DropdownItem,
SelectionBox,
};
static props = {
...standardViewProps,
allowSelectors: { type: Boolean, optional: true },
onSelectionChanged: { type: Function, optional: true },
readonly: { type: Boolean, optional: true },
showButtons: { type: Boolean, optional: true },
allowOpenAction: { type: Boolean, optional: true },
Model: Function,
Renderer: Function,
buttonTemplate: String,
archInfo: Object,
};
static defaultProps = {
allowSelectors: true,
createRecord: () => {},
selectRecord: () => {},
showButtons: true,
allowOpenAction: true,
};
setup() {
this.actionService = useService("action");
this.dialogService = useService("dialog");
this.orm = useService("orm");
this.rootRef = useRef("root");
this.archInfo = this.props.archInfo;
this.activeActions = this.archInfo.activeActions;
this.onOpenFormView = this.openRecord.bind(this);
this.editable = (!this.props.readonly && this.archInfo.editable) || false;
this.hasOpenFormViewButton = this.editable ? this.archInfo.openFormView : false;
this.model = useState(
useModelWithSampleData(this.props.Model, this.modelParams, this.modelOptions)
);
// In multi edition, we save or notify invalidity directly when a field is updated, which
// occurs on the change event for input fields. But we don't want to do it when clicking on
// "Discard". So we set a flag on mousedown (which triggers the update) to block the multi
// save or invalid notification.
// However, if the mouseup (and click) is done outside "Discard", we finally want to do it.
// We use `nextActionAfterMouseup` for this purpose: it registers a callback to execute if
// the mouseup following a mousedown on "Discard" isn't done on "Discard".
this.hasMousedownDiscard = false;
this.nextActionAfterMouseup = null;
this.optionalActiveFields = {};
this.editedRecord = null;
onWillRender(() => {
this.editedRecord = this.model.root.editedRecord;
});
onWillStart(async () => {
this.isExportEnable = await user.hasGroup("base.group_allow_export");
});
this.archiveEnabled =
"active" in this.props.fields
? !this.props.fields.active.readonly
: "x_active" in this.props.fields
? !this.props.fields.x_active.readonly
: false;
useSubEnv({ model: this.model }); // do this in useModelWithSampleData?
useViewButtons(this.rootRef, {
beforeExecuteAction: this.beforeExecuteActionButton.bind(this),
afterExecuteAction: this.afterExecuteActionButton.bind(this),
reload: () => this.model.load(),
});
const { setScrollFromState } = useSetupAction({
Registry / API
- Registry name
ListController- Category
—- Module
web- Slug
list-controller- Nav group
data_display