fields
forms
Reference
Odoo 19 fields — Reference (views)
Live preview
Interactive
Source excerpt
web/static/src/views/fields/reference/reference_field.js
import { Component, useState } from "@odoo/owl";
import { _t } from "@web/core/l10n/translation";
import { registry } from "@web/core/registry";
import { useRecordObserver } from "@web/model/relational_model/utils";
import { computeM2OProps, Many2One } from "../many2one/many2one";
import { extractM2OFieldProps, Many2OneField } from "../many2one/many2one_field";
/**
* @typedef ReferenceValue
* @property {string} resModel
* @property {number} resId
* @property {string} displayName
*/
/**
* 1. Reference field is a char field
* 2. Reference widget has model_field prop
* 3. Standard case
*/
/**
* This class represents a reference field widget. It can be used to display
* a reference field OR a char field.
* The res_model of the relation is defined either by the reference field itself
* or by the model_field prop.
*
* 1) Reference field is a char field
* We have to fetch the display name (name_get) of the referenced record.
*
* 2) Reference widget has model_field prop
* We have to fetch the technical name of the co model.
*
* 3) Standard case
* The value is already in record.data[fieldName]
*/
export class ReferenceField extends Component {
static template = "web.ReferenceField";
static components = { Many2One };
static props = {
...Many2OneField.props,
hideModel: { type: Boolean, optional: true },
modelField: { type: String, optional: true },
};
setup() {
/** @type {{formattedCharValue?: ReferenceValue, modelName?: string}} */
this.state = useState({
formattedCharValue: undefined, // Value extracted from reference char field
modelName: undefined, // Name get of the value of the model field
currentRelation: undefined,
});
if (this._isCharField(this.props)) {
/** Fetch the display name of the record referenced by the field */
let currentValue = undefined;
useRecordObserver(async (record, props) => {
if (currentValue !== record.data[props.name]) {
this.state.formattedCharValue = await this._fetchReferenceCharData(props);
currentValue = record.data[props.name];
}
});
} else if (this.props.modelField) {
/** Fetch the technical name of the co model */
useRecordObserver(async (record, props) => {
if (this.currentModelId !== record.data[props.modelField]?.id) {
this.state.modelName = await this._fetchModelTechnicalName(props);
if (this.currentModelId !== undefined) {
record.update({ [props.name]: false });
}
this.currentModelId = record.data[props.modelField]?.id;
}
});
}
}
get m2oProps() {
const value = this.getValue();
return {
...computeM2OProps(this.props),
relation: this.getRelation(),
value: value && { id: value.resId, display_name: value.displayName },
update: this.updateM2O.bind(this),
};
}
get selection() {
if (!this._isCharField(this.props) && !this.hideModelSelector) {
return this.props.record.fields[this.props.name].selection;
}
return [];
}
get hideModelSelector() {
return this.props.hideModel || this.props.modelField;
}
getRelation() {
const modelName = this.getModelName();
if (modelName) {
return modelName;
}
const value = this.getValue();
if (value && value.resModel) {
return value.resModel;
} else {
return this.state.currentRelation;
}
}
/**
* @returns {ReferenceValue|false}
*/
getValue() {
if (this._isCharField(this.props)) {
return this.state.formattedCharValue;
} else {
return this.props.record.data[this.props.name];
}
}
/**
Registry / API
- Registry name
reference- Category
fields- Module
web- Slug
reference- Nav group
forms