-
+
diff --git a/addons/web/static/src/core/file_input/file_input.js b/addons/web/static/src/core/file_input/file_input.js
index 2ebbb395222..80ddc25b6fd 100644
--- a/addons/web/static/src/core/file_input/file_input.js
+++ b/addons/web/static/src/core/file_input/file_input.js
@@ -34,6 +34,30 @@ export class FileInput extends Component {
});
}
+ get httpParams() {
+ const { resId, resModel } = this.props;
+ const params = {
+ csrf_token: odoo.csrf_token,
+ ufile: [...this.fileInputRef.el.files],
+ };
+ if (resModel) {
+ params.model = resModel;
+ }
+ if (resId !== undefined) {
+ params.id = resId;
+ }
+ return params;
+ }
+
+ async uploadFiles(params) {
+ const fileData = await this.http.post(this.props.route, params, "text");
+ const parsedFileData = JSON.parse(fileData);
+ if (parsedFileData.error) {
+ throw new Error(parsedFileData.error);
+ }
+ return parsedFileData;
+ }
+
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
@@ -46,22 +70,7 @@ export class FileInput extends Component {
* - resId: the id of the resModel target instance
*/
async onFileInputChange() {
- const { resId, resModel, route } = this.props;
- const params = {
- csrf_token: odoo.csrf_token,
- ufile: [...this.fileInputRef.el.files],
- };
- if (resModel) {
- params.model = resModel;
- }
- if (resId !== undefined) {
- params.id = resId;
- }
- const fileData = await this.http.post(route, params, "text");
- const parsedFileData = JSON.parse(fileData);
- if (parsedFileData.error) {
- throw new Error(parsedFileData.error);
- }
+ const parsedFileData = await this.uploadFiles(this.httpParams);
this.props.onUpload(parsedFileData);
}
diff --git a/addons/web/static/src/views/fields/input_field_hook.js b/addons/web/static/src/views/fields/input_field_hook.js
index 7fe73b57540..2c7119015f6 100644
--- a/addons/web/static/src/views/fields/input_field_hook.js
+++ b/addons/web/static/src/views/fields/input_field_hook.js
@@ -56,7 +56,9 @@ export function useInputField(params) {
try {
val = params.parse(val);
} catch (_e) {
- component.props.record.setInvalidField(component.props.name);
+ if (component.props.record) {
+ component.props.record.setInvalidField(component.props.name);
+ }
isInvalid = true;
}
}
@@ -101,7 +103,7 @@ export function useInputField(params) {
* If it is not such a case, we update the field with the new value.
*/
useEffect(() => {
- const isInvalid = component.props.record.isInvalid(component.props.name);
+ const isInvalid = component.props.record ? component.props.record.isInvalid(component.props.name) : false;
if (inputRef.el && !isDirty && !isInvalid) {
inputRef.el.value = params.getValue();
lastSetValue = inputRef.el.value;
@@ -133,7 +135,7 @@ export function useInputField(params) {
isInvalid = true;
if (urgent) {
return;
- } else {
+ } else if (component.props.record) {
component.props.record.setInvalidField(component.props.name);
}
}
diff --git a/addons/web/static/src/views/fields/text/text_field.js b/addons/web/static/src/views/fields/text/text_field.js
index d44e1353253..95466e97844 100644
--- a/addons/web/static/src/views/fields/text/text_field.js
+++ b/addons/web/static/src/views/fields/text/text_field.js
@@ -6,6 +6,7 @@ import { useInputField } from "../input_field_hook";
import { standardFieldProps } from "../standard_field_props";
import { TranslationButton } from "../translation_button";
import { useDynamicPlaceholder } from "../dynamicplaceholder_hook";
+import { parseInteger } from '../parsers';
const { Component, useEffect, onMounted, onWillUnmount, useRef } = owl;
@@ -67,7 +68,7 @@ export class TextField extends Component {
return 50;
}
get rowCount() {
- return 2;
+ return this.props.rowCount;
}
resize() {
@@ -105,23 +106,31 @@ TextField.template = "web.TextField";
TextField.components = {
TranslationButton,
};
-TextField.defaultProps = {dynamicPlaceholder: false};
+TextField.defaultProps = {
+ dynamicPlaceholder: false,
+ rowCount: 2,
+};
TextField.props = {
...standardFieldProps,
isTranslatable: { type: Boolean, optional: true },
placeholder: { type: String, optional: true },
dynamicPlaceholder: { type: Boolean, optional: true},
+ rowCount: { type: Number, optional: true },
};
TextField.displayName = _lt("Multiline Text");
TextField.supportedTypes = ["html", "text"];
TextField.extractProps = ({ attrs, field }) => {
- return {
+ const props = {
isTranslatable: field.translate,
placeholder: attrs.placeholder,
dynamicPlaceholder: attrs.options.dynamic_placeholder,
};
+ if (attrs.rows) {
+ props.rowCount = parseInteger(attrs.rows);
+ }
+ return props;
};
registry.category("fields").add("text", TextField);
@@ -131,8 +140,12 @@ export class ListTextField extends TextField {
return 0;
}
get rowCount() {
- return 1;
+ return this.props.rowCount;
}
}
+ListTextField.defaultProps = {
+ ...TextField.defaultProps,
+ rowCount: 1,
+};
registry.category("fields").add("list.text", ListTextField);
diff --git a/addons/web/static/tests/views/fields/text_field_tests.js b/addons/web/static/tests/views/fields/text_field_tests.js
index 62ec480b07a..76459e2772e 100644
--- a/addons/web/static/tests/views/fields/text_field_tests.js
+++ b/addons/web/static/tests/views/fields/text_field_tests.js
@@ -240,6 +240,28 @@ QUnit.module("Fields", (hooks) => {
);
});
+ QUnit.test("set row on text fields", async function (assert) {
+ serverData.models.partner.fields.foo.type = "text";
+
+ await makeView({
+ type: "form",
+ resModel: "partner",
+ resId: 1,
+ serverData,
+ arch: `
+
`,
+ });
+
+ const textarea = target.querySelector("textarea");
+ assert.strictEqual(
+ textarea.rows,
+ 4,
+ "rowCount should be the one set on the field",
+ );
+ });
+
QUnit.test(
"autoresize of text fields is done when switching to edit mode",
async function (assert) {