OWL
inputs
Auto Complete
Odoo 19 OWL component — Auto Complete (core)
Live preview
Interactive
Source excerpt
web/static/src/core/autocomplete/autocomplete.js
import { Deferred } from "@web/core/utils/concurrency";
import { useAutofocus, useForwardRefToParent, useService } from "@web/core/utils/hooks";
import { isScrollableY, scrollTo } from "@web/core/utils/scrolling";
import { useDebounced } from "@web/core/utils/timing";
import { getActiveHotkey } from "@web/core/hotkeys/hotkey_service";
import { usePosition } from "@web/core/position/position_hook";
import { Component, onWillUpdateProps, useExternalListener, useRef, useState } from "@odoo/owl";
import { mergeClasses } from "@web/core/utils/classname";
export class AutoComplete extends Component {
static template = "web.AutoComplete";
static props = {
value: { type: String, optional: true },
id: { type: String, optional: true },
sources: {
type: Array,
element: {
type: Object,
shape: {
placeholder: { type: String, optional: true },
options: [Array, Function],
optionSlot: { type: String, optional: true },
},
},
},
placeholder: { type: String, optional: true },
title: { type: String, optional: true },
autocomplete: { type: String, optional: true },
autoSelect: { type: Boolean, optional: true },
resetOnSelect: { type: Boolean, optional: true },
onInput: { type: Function, optional: true },
onCancel: { type: Function, optional: true },
onChange: { type: Function, optional: true },
onBlur: { type: Function, optional: true },
onFocus: { type: Function, optional: true },
searchOnInputClick: { type: Boolean, optional: true },
input: { type: Function, optional: true },
inputDebounceDelay: { type: Number, optional: true },
dropdown: { type: Boolean, optional: true },
autofocus: { type: Boolean, optional: true },
class: { type: String, optional: true },
slots: { type: Object, optional: true },
menuPositionOptions: { type: Object, optional: true },
menuCssClass: { type: [String, Array, Object], optional: true },
selectOnBlur: { type: Boolean, optional: true },
};
static defaultProps = {
value: "",
placeholder: "",
title: "",
autocomplete: "new-password",
autoSelect: false,
dropdown: true,
onInput: () => {},
onCancel: () => {},
onChange: () => {},
onBlur: () => {},
onFocus: () => {},
searchOnInputClick: true,
inputDebounceDelay: 250,
menuPositionOptions: {},
menuCssClass: {},
};
get timeout() {
return this.props.inputDebounceDelay;
}
setup() {
this.nextSourceId = 0;
this.nextOptionId = 0;
this.sources = [];
this.inEdition = false;
this.mouseSelectionActive = false;
this.isOptionSelected = false;
this.state = useState({
navigationRev: 0,
optionsRev: 0,
open: false,
activeSourceOption: null,
value: this.props.value,
});
this.inputRef = useForwardRefToParent("input");
this.listRef = useRef("sourcesList");
if (this.props.autofocus) {
useAutofocus({ refName: "input" });
}
this.root = useRef("root");
this.debouncedProcessInput = useDebounced(async () => {
const currentPromise = this.pendingPromise;
this.pendingPromise = null;
this.props.onInput({
inputValue: this.inputRef.el.value,
});
try {
await this.open(true);
currentPromise.resolve();
} catch {
currentPromise.reject();
} finally {
if (currentPromise === this.loadingPromise) {
this.loadingPromise = null;
}
}
}, this.timeout);
useExternalListener(window, "scroll", this.externalClose, true);
useExternalListener(window, "pointerdown", this.externalClose, true);
useExternalListener(window, "mousemove", () => (this.mouseSelectionActive = true), true);
this.hotkey = useService("hotkey");
this.hotkeysToRemove = [];
onWillUpdateProps((nextProps) => {
if (this.props.value !== nextProps.value || this.forceValFromProp) {
this.forceValFromProp = false;
if (!this.inEdition) {
Registry / API
- Registry name
AutoComplete- Category
—- Module
web- Slug
auto-complete- Nav group
inputs