-
+
+
diff --git a/addons/web/static/src/views/fields/color_picker/color_picker_field.js b/addons/web/static/src/views/fields/color_picker/color_picker_field.js
index b71f763358b..01c748121e0 100644
--- a/addons/web/static/src/views/fields/color_picker/color_picker_field.js
+++ b/addons/web/static/src/views/fields/color_picker/color_picker_field.js
@@ -19,6 +19,7 @@ ColorPickerField.components = {
ColorPickerField.props = {
...standardFieldProps,
};
+
ColorPickerField.supportedTypes = ["integer"];
ColorPickerField.RECORD_COLORS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
diff --git a/addons/web/static/src/views/fields/date/date_field.js b/addons/web/static/src/views/fields/date/date_field.js
index e68eb60ddc8..eb2ddeccd1d 100644
--- a/addons/web/static/src/views/fields/date/date_field.js
+++ b/addons/web/static/src/views/fields/date/date_field.js
@@ -9,6 +9,9 @@ import { standardFieldProps } from "../standard_field_props";
const { Component } = owl;
export class DateField extends Component {
+ get isDateTime() {
+ return this.props.record.fields[this.props.name].type === "datetime";
+ }
get date() {
return this.props.value && this.props.value.startOf("day");
}
@@ -16,7 +19,7 @@ export class DateField extends Component {
get formattedValue() {
return formatDate(this.props.value, {
// get local date if field type is datetime
- timezone: this.props.isDateTime,
+ timezone: this.isDateTime,
});
}
@@ -33,21 +36,20 @@ DateField.components = {
};
DateField.props = {
...standardFieldProps,
- isDateTime: { type: Boolean, optional: true },
pickerOptions: { type: Object, optional: true },
+ placeholder: { type: String, optional: true },
};
DateField.defaultProps = {
- isDateTime: false,
pickerOptions: {},
};
DateField.displayName = _lt("Date");
DateField.supportedTypes = ["date", "datetime"];
-DateField.extractProps = (fieldName, record, attrs) => {
+DateField.extractProps = ({ attrs }) => {
return {
- isDateTime: record.fields[fieldName].type === "datetime",
pickerOptions: attrs.options.datepicker,
+ placeholder: attrs.placeholder,
};
};
diff --git a/addons/web/static/src/views/fields/date/date_field.xml b/addons/web/static/src/views/fields/date/date_field.xml
index 196c44146fb..5326d2db462 100644
--- a/addons/web/static/src/views/fields/date/date_field.xml
+++ b/addons/web/static/src/views/fields/date/date_field.xml
@@ -9,6 +9,7 @@
diff --git a/addons/web/static/src/views/fields/daterange/daterange_field.js b/addons/web/static/src/views/fields/daterange/daterange_field.js
index 5117e551637..34502b2092f 100644
--- a/addons/web/static/src/views/fields/daterange/daterange_field.js
+++ b/addons/web/static/src/views/fields/daterange/daterange_field.js
@@ -4,6 +4,7 @@ import { localization } from "@web/core/l10n/localization";
import { registry } from "@web/core/registry";
import { loadJS } from "@web/core/assets";
import { useService } from "@web/core/utils/hooks";
+import { standardFieldProps } from "../standard_field_props";
const { Component, onWillStart, useExternalListener, useRef, useEffect } = owl;
const formatters = registry.category("formatters");
@@ -61,13 +62,22 @@ export class DateRangeField extends Component {
get formattedValue() {
return this.formatValue(this.props.formatType, this.props.value);
}
-
get formattedEndDate() {
- return this.formatValue(this.props.formatType, this.props.endDate);
+ return this.formatValue(this.props.formatType, this.endDate);
}
-
get formattedStartDate() {
- return this.formatValue(this.props.formatType, this.props.startDate);
+ return this.formatValue(this.props.formatType, this.startDate);
+ }
+ get startDate() {
+ return this.props.record.data[this.props.relatedStartDateField || this.props.name];
+ }
+ get endDate() {
+ return this.props.record.data[this.props.relatedEndDateField || this.props.name];
+ }
+ get relatedDateRangeField() {
+ return this.props.relatedStartDateField
+ ? this.props.relatedStartDateField
+ : this.props.relatedEndDateField;
}
formatValue(format, value) {
@@ -76,18 +86,25 @@ export class DateRangeField extends Component {
try {
formattedValue = formatter(value, { timezone: this.isDateTime });
} catch {
- this.props.invalidate();
+ this.props.record.setInvalidField(this.props.name);
}
return formattedValue;
}
+ updateRange(start, end) {
+ return this.props.record.update({
+ [this.props.relatedStartDateField || this.props.name]: start,
+ [this.props.relatedEndDateField || this.props.name]: end,
+ });
+ }
+
onChangeInput(ev) {
const parse = parsers.get(this.props.formatType);
let value;
try {
value = parse(ev.target.value, { timezone: this.isDateTime });
} catch {
- this.props.invalidate();
+ this.props.record.setInvalidField(this.props.name);
return;
}
this.props.update(value);
@@ -114,9 +131,9 @@ export class DateRangeField extends Component {
timezone: this.isDateTime,
});
});
- await this.props.updateRange(dates[0], dates[1]);
+ await this.updateRange(dates[0], dates[1]);
const input = document.querySelector(
- `.o_field_daterange[name='${this.props.relatedDateRange}'] input`
+ `.o_field_daterange[name='${this.relatedDateRangeField}'] input`
);
const target = window.$(input).data("daterangepicker");
target.setStartDate(picker.startDate);
@@ -130,25 +147,22 @@ export class DateRangeField extends Component {
}
}
DateRangeField.template = "web.DateRangeField";
+DateRangeField.props = {
+ ...standardFieldProps,
+ relatedEndDateField: { type: String, optional: true },
+ relatedStartDateField: { type: String, optional: true },
+ formatType: { type: String, optional: true },
+ placeholder: { type: String, optional: true },
+};
DateRangeField.supportedTypes = ["date", "datetime"];
-DateRangeField.extractProps = (fieldName, record, attrs) => {
- const relatedEndDate = attrs.options.related_end_date;
- const relatedStartDate = attrs.options.related_start_date;
-
+DateRangeField.extractProps = ({ attrs, field }) => {
return {
- relatedDateRange: relatedEndDate ? relatedEndDate : relatedStartDate,
- endDate: record.data[relatedEndDate || fieldName],
- formatType: attrs.options.format_type || record.fields[fieldName].type,
- invalidate: () => record.setInvalidField(fieldName),
- startDate: record.data[relatedStartDate || fieldName],
- async updateRange(start, end) {
- await record.update({
- [relatedStartDate || fieldName]: start,
- [relatedEndDate || fieldName]: end,
- });
- },
+ relatedEndDateField: attrs.options.related_end_date,
+ relatedStartDateField: attrs.options.related_start_date,
+ formatType: attrs.options.format_type || field.type,
+ placeholder: attrs.placeholder,
};
};
diff --git a/addons/web/static/src/views/fields/daterange/daterange_field.xml b/addons/web/static/src/views/fields/daterange/daterange_field.xml
index b0beea56949..bd56ca488ba 100644
--- a/addons/web/static/src/views/fields/daterange/daterange_field.xml
+++ b/addons/web/static/src/views/fields/daterange/daterange_field.xml
@@ -6,7 +6,7 @@
-
+
diff --git a/addons/web/static/src/views/fields/datetime/datetime_field.js b/addons/web/static/src/views/fields/datetime/datetime_field.js
index 156516623d7..ffdc6fe7a06 100644
--- a/addons/web/static/src/views/fields/datetime/datetime_field.js
+++ b/addons/web/static/src/views/fields/datetime/datetime_field.js
@@ -27,6 +27,7 @@ DateTimeField.components = {
DateTimeField.props = {
...standardFieldProps,
pickerOptions: { type: Object, optional: true },
+ placeholder: { type: String, optional: true },
};
DateTimeField.defaultProps = {
pickerOptions: {},
@@ -35,9 +36,10 @@ DateTimeField.defaultProps = {
DateTimeField.displayName = _lt("Date & Time");
DateTimeField.supportedTypes = ["datetime"];
-DateTimeField.extractProps = (fieldName, record, attrs) => {
+DateTimeField.extractProps = ({ attrs }) => {
return {
pickerOptions: attrs.options.datepicker,
+ placeholder: attrs.placeholder,
};
};
diff --git a/addons/web/static/src/views/fields/datetime/datetime_field.xml b/addons/web/static/src/views/fields/datetime/datetime_field.xml
index de7e2212277..ea08911fde1 100644
--- a/addons/web/static/src/views/fields/datetime/datetime_field.xml
+++ b/addons/web/static/src/views/fields/datetime/datetime_field.xml
@@ -9,6 +9,7 @@
diff --git a/addons/web/static/src/views/fields/domain/domain_field.js b/addons/web/static/src/views/fields/domain/domain_field.js
index a0576231ca0..7bcf9d166c9 100644
--- a/addons/web/static/src/views/fields/domain/domain_field.js
+++ b/addons/web/static/src/views/fields/domain/domain_field.js
@@ -40,20 +40,31 @@ export class DomainField extends Component {
ev.detail.proms.push(prom);
await prom;
if (!this.state.isValid) {
- this.props.invalidate();
+ this.props.record.setInvalidField(this.props.name);
}
}
});
}
+ getContext(p) {
+ return p.record.getFieldContext(p.name);
+ }
+ getResModel(p) {
+ let resModel = p.resModel;
+ if (p.record.fieldNames.includes(resModel)) {
+ resModel = p.record.data[resModel];
+ }
+ return resModel;
+ }
+
onButtonClick() {
this.dialog.add(SelectCreateDialog, {
title: this.env._t("Selected records"),
noCreate: true,
multiSelect: false,
- resModel: this.props.resModel,
- domain: this.getDomain(this.props.value).toList(this.props.context) || [],
- context: this.props.context || {},
+ resModel: this.getResModel(this.props),
+ domain: this.getDomain(this.props.value).toList(this.getContext(this.props)) || [],
+ context: this.getContext(this.props) || {},
});
}
get isValidDomain() {
@@ -70,16 +81,19 @@ export class DomainField extends Component {
return new Domain(value || "[]");
}
async loadCount(props) {
- if (!props.resModel) {
+ if (!this.getResModel(props)) {
Object.assign(this.state, { recordCount: 0, isValid: true });
}
let recordCount;
try {
- const domain = this.getDomain(props.value).toList(props.context);
- recordCount = await this.orm.silent.call(props.resModel, "search_count", [domain], {
- context: props.context,
- });
+ const domain = this.getDomain(props.value).toList(this.getContext(props));
+ recordCount = await this.orm.silent.call(
+ this.getResModel(props),
+ "search_count",
+ [domain],
+ { context: this.getContext(props) }
+ );
} catch (_e) {
// WOWL TODO: rethrow error when not the expected type
Object.assign(this.state, { recordCount: 0, isValid: false });
@@ -100,29 +114,16 @@ DomainField.components = {
};
DomainField.props = {
...standardFieldProps,
- context: { type: Object, optional: true },
- invalidate: { type: Function, optional: true },
resModel: { type: String, optional: true },
};
-DomainField.defaultProps = {
- context: {},
- invalidate: () => {},
-};
DomainField.displayName = _lt("Domain");
DomainField.supportedTypes = ["char"];
DomainField.isEmpty = () => false;
-DomainField.extractProps = (fieldName, record, attrs) => {
- let resModel = attrs.options.model;
- if (record.fieldNames.includes(resModel)) {
- resModel = record.data[resModel];
- }
-
+DomainField.extractProps = ({ attrs }) => {
return {
- context: record.getFieldContext(fieldName),
- invalidate: () => record.setInvalidField(fieldName),
- resModel,
+ resModel: attrs.options.model,
};
};
diff --git a/addons/web/static/src/views/fields/domain/domain_field.xml b/addons/web/static/src/views/fields/domain/domain_field.xml
index 9f38f0aaf79..f7670b6c9dd 100644
--- a/addons/web/static/src/views/fields/domain/domain_field.xml
+++ b/addons/web/static/src/views/fields/domain/domain_field.xml
@@ -3,9 +3,9 @@
-
+
{
+EmailField.extractProps = ({ attrs }) => {
return {
placeholder: attrs.placeholder,
};
diff --git a/addons/web/static/src/views/fields/field.js b/addons/web/static/src/views/fields/field.js
index 9282d2af818..14ba2e2c746 100644
--- a/addons/web/static/src/views/fields/field.js
+++ b/addons/web/static/src/views/fields/field.js
@@ -132,12 +132,16 @@ export class Field extends Component {
decorationMap[decoName] = value;
}
- let propsFromAttrs = {};
- if (this.FieldComponent.extractProps) {
- const attrs = Object.assign({}, this.props.attrs || fieldInfo.rawAttrs, {
- options: fieldInfo.options || {},
+ let propsFromAttrs = fieldInfo.propsFromAttrs;
+ if (this.props.attrs) {
+ const extractProps = this.FieldComponent.extractProps || (() => ({}));
+ propsFromAttrs = extractProps({
+ field,
+ attrs: {
+ ...this.props.attrs,
+ options: evaluateExpr(this.props.attrs.options || "{}"),
+ },
});
- propsFromAttrs = this.FieldComponent.extractProps(this.props.name, record, attrs);
}
const props = { ...this.props };
@@ -221,7 +225,18 @@ Field.parseFieldNode = function (node, models, modelName, viewType, jsClass) {
continue;
}
- fieldInfo.rawAttrs[attribute.name] = attribute.value;
+ if (!attribute.name.startsWith("t-att")) {
+ fieldInfo.rawAttrs[attribute.name] = attribute.value;
+ }
+ }
+
+ if (viewType !== "kanban") {
+ // FIXME WOWL: find a better solution
+ const extractProps = fieldInfo.FieldComponent.extractProps || (() => ({}));
+ fieldInfo.propsFromAttrs = extractProps({
+ field,
+ attrs: { ...fieldInfo.rawAttrs, options: fieldInfo.options },
+ });
}
if (fieldInfo.modifiers.invisible !== true && X2M_TYPES.includes(field.type)) {
diff --git a/addons/web/static/src/views/fields/float/float_field.js b/addons/web/static/src/views/fields/float/float_field.js
index e23967678a0..b835343fcf5 100644
--- a/addons/web/static/src/views/fields/float/float_field.js
+++ b/addons/web/static/src/views/fields/float/float_field.js
@@ -52,28 +52,23 @@ FloatField.props = {
inputType: { type: String, optional: true },
step: { type: Number, optional: true },
digits: { type: Array, optional: true },
- invalidate: { type: Function, optional: true },
placeholder: { type: String, optional: true },
};
FloatField.defaultProps = {
inputType: "text",
- invalidate: () => {},
};
FloatField.displayName = _lt("Float");
FloatField.supportedTypes = ["float"];
FloatField.isEmpty = () => false;
-FloatField.extractProps = (fieldName, record, attrs) => {
+FloatField.extractProps = ({ attrs, field }) => {
return {
- invalidate: () => record.setInvalidField(fieldName),
inputType: attrs.options.type,
step: attrs.options.step,
// Sadly, digits param was available as an option and an attr.
// The option version could be removed with some xml refactoring.
- digits:
- (attrs.digits ? JSON.parse(attrs.digits) : attrs.options.digits) ||
- record.fields[fieldName].digits,
+ digits: (attrs.digits ? JSON.parse(attrs.digits) : attrs.options.digits) || field.digits,
placeholder: attrs.placeholder,
};
};
diff --git a/addons/web/static/src/views/fields/float_factor/float_factor_field.js b/addons/web/static/src/views/fields/float_factor/float_factor_field.js
index 054b9582363..0bf1bd4a967 100644
--- a/addons/web/static/src/views/fields/float_factor/float_factor_field.js
+++ b/addons/web/static/src/views/fields/float_factor/float_factor_field.js
@@ -2,7 +2,6 @@
import { registry } from "@web/core/registry";
import { FloatField } from "../float/float_field";
-import { standardFieldProps } from "../standard_field_props";
const { Component } = owl;
export class FloatFactorField extends Component {
@@ -20,26 +19,20 @@ export class FloatFactorField extends Component {
FloatFactorField.template = "web.FloatFactorField";
FloatFactorField.components = { FloatField };
FloatFactorField.props = {
- ...standardFieldProps,
- inputType: { type: String, optional: true },
- digits: { type: Array, optional: true },
- invalidate: { type: Function, optional: true },
+ ...FloatField.props,
factor: { type: Number, optional: true },
};
FloatFactorField.defaultProps = {
- inputType: "text",
- invalidate: () => {},
+ ...FloatField.defaultProps,
factor: 1,
};
FloatFactorField.supportedTypes = ["float"];
FloatFactorField.isEmpty = () => false;
-FloatFactorField.extractProps = (fieldName, record, attrs) => {
+FloatFactorField.extractProps = ({ attrs, field }) => {
return {
- invalidate: record.setInvalidField.bind(record),
- inputType: attrs.options.type,
- digits: attrs.digits ? JSON.parse(attrs.digits) : attrs.options.digits,
+ ...FloatField.extractProps({ attrs, field }),
factor: attrs.options.factor,
};
};
diff --git a/addons/web/static/src/views/fields/float_time/float_time_field.js b/addons/web/static/src/views/fields/float_time/float_time_field.js
index bdd517b0810..f3cc12aa417 100644
--- a/addons/web/static/src/views/fields/float_time/float_time_field.js
+++ b/addons/web/static/src/views/fields/float_time/float_time_field.js
@@ -28,20 +28,15 @@ export class FloatTimeField extends Component {
FloatTimeField.template = "web.FloatTimeField";
FloatTimeField.props = {
...standardFieldProps,
- invalidate: { type: Function, optional: true },
placeholder: { type: String, optional: true },
};
-FloatTimeField.defaultProps = {
- invalidate: () => {},
-};
FloatTimeField.displayName = _lt("Time");
FloatTimeField.supportedTypes = ["float"];
FloatTimeField.isEmpty = () => false;
-FloatTimeField.extractProps = (fieldName, record, attrs) => {
+FloatTimeField.extractProps = ({ attrs }) => {
return {
- invalidate: () => record.setInvalidField(fieldName),
placeholder: attrs.placeholder,
};
};
diff --git a/addons/web/static/src/views/fields/float_toggle/float_toggle_field.js b/addons/web/static/src/views/fields/float_toggle/float_toggle_field.js
index b08a7628e95..a5d298fd455 100644
--- a/addons/web/static/src/views/fields/float_toggle/float_toggle_field.js
+++ b/addons/web/static/src/views/fields/float_toggle/float_toggle_field.js
@@ -42,11 +42,9 @@ FloatToggleField.defaultProps = {
FloatToggleField.supportedTypes = ["float"];
FloatToggleField.isEmpty = () => false;
-FloatToggleField.extractProps = (fieldName, record, attrs) => {
+FloatToggleField.extractProps = ({ attrs, field }) => {
return {
- digits:
- (attrs.digits ? JSON.parse(attrs.digits) : attrs.options.digits) ||
- record.fields[fieldName].digits,
+ digits: (attrs.digits ? JSON.parse(attrs.digits) : attrs.options.digits) || field.digits,
range: attrs.options.range,
factor: attrs.options.factor,
disableReadOnly: attrs.options.force_button || false,
diff --git a/addons/web/static/src/views/fields/font_selection/font_selection_field.js b/addons/web/static/src/views/fields/font_selection/font_selection_field.js
index bf19642f06b..fb10e1a7401 100644
--- a/addons/web/static/src/views/fields/font_selection/font_selection_field.js
+++ b/addons/web/static/src/views/fields/font_selection/font_selection_field.js
@@ -8,8 +8,14 @@ import { formatSelection } from "../formatters";
const { Component } = owl;
export class FontSelectionField extends Component {
+ get options() {
+ return Array.from(this.props.record.fields[this.props.name].selection);
+ }
+ get isRequired() {
+ return this.props.record.isRequired(this.props.name);
+ }
get string() {
- return formatSelection(this.props.value, { selection: this.props.options });
+ return formatSelection(this.props.value, { selection: this.options });
}
stringify(value) {
@@ -28,21 +34,14 @@ export class FontSelectionField extends Component {
FontSelectionField.template = "web.FontSelectionField";
FontSelectionField.props = {
...standardFieldProps,
- options: Object,
placeholder: { type: String, optional: true },
- required: { type: Boolean, optional: true },
-};
-FontSelectionField.defaultProps = {
- required: false,
};
FontSelectionField.displayName = _lt("Font Selection");
FontSelectionField.supportedTypes = ["selection"];
-FontSelectionField.extractProps = (fieldName, record, attrs) => {
+FontSelectionField.extractProps = ({ attrs }) => {
return {
- options: Array.from(record.fields[fieldName].selection),
- required: record.isRequired(fieldName),
placeholder: attrs.placeholder,
};
};
diff --git a/addons/web/static/src/views/fields/font_selection/font_selection_field.xml b/addons/web/static/src/views/fields/font_selection/font_selection_field.xml
index 73afb687392..93ae586dbc1 100644
--- a/addons/web/static/src/views/fields/font_selection/font_selection_field.xml
+++ b/addons/web/static/src/views/fields/font_selection/font_selection_field.xml
@@ -3,23 +3,23 @@
-
+
-