[REF] web: convert FieldBoolean to owl
This commit does 4 things: - adds a file for deprecated legacy fields. - moves legacy FieldBoolean into this file. - converts FieldBoolean to owl component. - adds a new xml file for component fields' templates. task id: 2193996
This commit is contained in:
@@ -23,6 +23,7 @@ This module provides the core of the Odoo Web Client.
|
||||
'qweb': [
|
||||
"static/src/xml/base.xml",
|
||||
"static/src/xml/chart.xml",
|
||||
"static/src/xml/fields.xml",
|
||||
"static/src/xml/file_upload_progress_bar.xml",
|
||||
"static/src/xml/file_upload_progress_card.xml",
|
||||
"static/src/xml/kanban.xml",
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// /!\ DEPRECATED
|
||||
//
|
||||
// Legacy Field Widgets are added in this file when they are converted into
|
||||
// Owl Component.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
odoo.define('web.basic_fields.deprecated', function (require) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* This module contains most of the basic (meaning: non relational) field
|
||||
* widgets. Field widgets are supposed to be used in views inheriting from
|
||||
* BasicView, so, they can work with the records obtained from a BasicModel.
|
||||
*/
|
||||
|
||||
var AbstractField = require('web.AbstractField');
|
||||
var core = require('web.core');
|
||||
|
||||
var _lt = core._lt;
|
||||
|
||||
var FieldBoolean = AbstractField.extend({
|
||||
className: 'o_field_boolean',
|
||||
description: _lt("Checkbox"),
|
||||
events: _.extend({}, AbstractField.prototype.events, {
|
||||
change: '_onChange',
|
||||
}),
|
||||
supportedFieldTypes: ['boolean'],
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Toggle the checkbox if it is activated due to a click on itself.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
activate: function (options) {
|
||||
var activated = this._super.apply(this, arguments);
|
||||
// The formatValue of boolean fields renders HTML elements similar to
|
||||
// the one rendered by the widget itself. Even though the event might
|
||||
// have been fired on the non-widget version of this field, we can still
|
||||
// test the presence of its custom class.
|
||||
if (activated && options && options.event && $(options.event.target).closest('.custom-control.custom-checkbox').length) {
|
||||
this._setValue(!this.value); // Toggle the checkbox
|
||||
}
|
||||
return activated;
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @returns {jQuery} the focusable checkbox input
|
||||
*/
|
||||
getFocusableElement: function () {
|
||||
return this.mode === 'readonly' ? $() : this.$input;
|
||||
},
|
||||
/**
|
||||
* A boolean field is always set since false is a valid value.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
isSet: function () {
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* When the checkbox is rerendered, we need to check if it was the actual
|
||||
* origin of the reset. If it is, we need to activate it back so it looks
|
||||
* like it was not rerendered but is still the same input.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
reset: function (record, event) {
|
||||
var rendered = this._super.apply(this, arguments);
|
||||
if (event && event.target.name === this.name) {
|
||||
this.activate();
|
||||
}
|
||||
return rendered;
|
||||
},
|
||||
/**
|
||||
* Associates the 'for' attribute of the internal label.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
setIDForLabel: function (id) {
|
||||
this._super.apply(this, arguments);
|
||||
this.$('.custom-control-label').attr('for', id);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The actual checkbox is designed in css to have full control over its
|
||||
* appearance, as opposed to letting the browser and the os decide how
|
||||
* a checkbox should look. The actual input is disabled and hidden. In
|
||||
* readonly mode, the checkbox is disabled.
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_render: function () {
|
||||
var $checkbox = this._formatValue(this.value);
|
||||
this.$input = $checkbox.find('input');
|
||||
this.$input.prop('disabled', this.mode === 'readonly');
|
||||
this.$el.addClass($checkbox.attr('class'));
|
||||
this.$el.empty().append($checkbox.contents());
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Properly update the value when the checkbox is (un)ticked to trigger
|
||||
* possible onchanges.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onChange: function () {
|
||||
this._setValue(this.$input[0].checked);
|
||||
},
|
||||
/**
|
||||
* Implement keyboard movements. Mostly useful for its environment, such
|
||||
* as a list view.
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
* @param {KeyEvent} ev
|
||||
*/
|
||||
_onKeydown: function (ev) {
|
||||
switch (ev.which) {
|
||||
case $.ui.keyCode.ENTER:
|
||||
// prevent subsequent 'click' event (see _onKeydown of AbstractField)
|
||||
ev.preventDefault();
|
||||
this.$input.prop('checked', !this.value);
|
||||
this._setValue(!this.value);
|
||||
return;
|
||||
case $.ui.keyCode.UP:
|
||||
case $.ui.keyCode.RIGHT:
|
||||
case $.ui.keyCode.DOWN:
|
||||
case $.ui.keyCode.LEFT:
|
||||
ev.preventDefault();
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
FieldBoolean: FieldBoolean,
|
||||
};
|
||||
|
||||
});
|
||||
@@ -11,6 +11,7 @@ var AbstractField = require('web.AbstractField');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var datepicker = require('web.datepicker');
|
||||
var deprecatedFields = require('web.basic_fields.deprecated');
|
||||
var dom = require('web.dom');
|
||||
var Domain = require('web.Domain');
|
||||
var DomainSelector = require('web.DomainSelector');
|
||||
@@ -24,6 +25,8 @@ var field_utils = require('web.field_utils');
|
||||
var time = require('web.time');
|
||||
var ColorpickerDialog = require('web.ColorpickerDialog');
|
||||
|
||||
let FieldBoolean = deprecatedFields.FieldBoolean;
|
||||
|
||||
require("web.zoomodoo");
|
||||
|
||||
var qweb = core.qweb;
|
||||
@@ -986,134 +989,6 @@ var FieldMonetary = NumericField.extend({
|
||||
},
|
||||
});
|
||||
|
||||
var FieldBoolean = AbstractField.extend({
|
||||
className: 'o_field_boolean',
|
||||
description: _lt("Checkbox"),
|
||||
events: _.extend({}, AbstractField.prototype.events, {
|
||||
change: '_onChange',
|
||||
}),
|
||||
supportedFieldTypes: ['boolean'],
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Toggle the checkbox if it is activated due to a click on itself.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
activate: function (options) {
|
||||
var activated = this._super.apply(this, arguments);
|
||||
// The formatValue of boolean fields renders HTML elements similar to
|
||||
// the one rendered by the widget itself. Even though the event might
|
||||
// have been fired on the non-widget version of this field, we can still
|
||||
// test the presence of its custom class.
|
||||
if (activated && options && options.event && $(options.event.target).closest('.custom-control.custom-checkbox').length) {
|
||||
this._setValue(!this.value); // Toggle the checkbox
|
||||
}
|
||||
return activated;
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @returns {jQuery} the focusable checkbox input
|
||||
*/
|
||||
getFocusableElement: function () {
|
||||
return this.mode === 'readonly' ? $() : this.$input;
|
||||
},
|
||||
/**
|
||||
* A boolean field is always set since false is a valid value.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
isSet: function () {
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* When the checkbox is rerendered, we need to check if it was the actual
|
||||
* origin of the reset. If it is, we need to activate it back so it looks
|
||||
* like it was not rerendered but is still the same input.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
reset: function (record, event) {
|
||||
var rendered = this._super.apply(this, arguments);
|
||||
if (event && event.target.name === this.name) {
|
||||
this.activate();
|
||||
}
|
||||
return rendered;
|
||||
},
|
||||
/**
|
||||
* Associates the 'for' attribute of the internal label.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
setIDForLabel: function (id) {
|
||||
this._super.apply(this, arguments);
|
||||
this.$('.custom-control-label').attr('for', id);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The actual checkbox is designed in css to have full control over its
|
||||
* appearance, as opposed to letting the browser and the os decide how
|
||||
* a checkbox should look. The actual input is disabled and hidden. In
|
||||
* readonly mode, the checkbox is disabled.
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_render: function () {
|
||||
var $checkbox = this._formatValue(this.value);
|
||||
this.$input = $checkbox.find('input');
|
||||
this.$input.prop('disabled', this.mode === 'readonly');
|
||||
this.$el.addClass($checkbox.attr('class'));
|
||||
this.$el.empty().append($checkbox.contents());
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Properly update the value when the checkbox is (un)ticked to trigger
|
||||
* possible onchanges.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onChange: function () {
|
||||
this._setValue(this.$input[0].checked);
|
||||
},
|
||||
/**
|
||||
* Implement keyboard movements. Mostly useful for its environment, such
|
||||
* as a list view.
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
* @param {KeyEvent} ev
|
||||
*/
|
||||
_onKeydown: function (ev) {
|
||||
switch (ev.which) {
|
||||
case $.ui.keyCode.ENTER:
|
||||
// prevent subsequent 'click' event (see _onKeydown of AbstractField)
|
||||
ev.preventDefault();
|
||||
this.$input.prop('checked', !this.value);
|
||||
this._setValue(!this.value);
|
||||
return;
|
||||
case $.ui.keyCode.UP:
|
||||
case $.ui.keyCode.RIGHT:
|
||||
case $.ui.keyCode.DOWN:
|
||||
case $.ui.keyCode.LEFT:
|
||||
ev.preventDefault();
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
var FieldInteger = NumericField.extend({
|
||||
description: _lt("Integer"),
|
||||
className: 'o_field_integer o_field_number',
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
odoo.define('web.basic_fields_owl', function (require) {
|
||||
"use strict";
|
||||
|
||||
const AbstractField = require('web.AbstractFieldOwl');
|
||||
const CustomCheckbox = require('web.CustomCheckbox');
|
||||
const core = require('web.core');
|
||||
|
||||
const _lt = core._lt;
|
||||
|
||||
class FieldBoolean extends AbstractField {
|
||||
patched() {
|
||||
super.patched();
|
||||
if (this.props.event && this.props.event.target === this) {
|
||||
this.activate();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Public
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @returns {HTMLElement|null} the focusable checkbox input
|
||||
*/
|
||||
get focusableElement() {
|
||||
return this.mode === 'readonly' ? null : this.el.querySelector('input');
|
||||
}
|
||||
/**
|
||||
* A boolean field is always set since false is a valid value.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
get isSet() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Toggle the checkbox if it is activated due to a click on itself.
|
||||
*
|
||||
* @override
|
||||
* @param {Object} [options]
|
||||
* @param {Event} [options.event] the event which fired this activation
|
||||
* @returns {boolean} true if the component was activated, false if the
|
||||
* focusable element was not found or invisible
|
||||
*/
|
||||
activate(options) {
|
||||
const activated = super.activate(options);
|
||||
// The event might have been fired on the non field version of
|
||||
// this field, we can still test the presence of its custom class.
|
||||
if (activated && options && options.event && options.event.target
|
||||
.closest('.custom-control.custom-checkbox')) {
|
||||
this._setValue(!this.value); // Toggle the checkbox
|
||||
}
|
||||
return activated;
|
||||
}
|
||||
/**
|
||||
* Associates the 'for' attribute of the internal label.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
setIdForLabel(id) {
|
||||
super.setIdForLabel(id);
|
||||
this.el.querySelector('label').setAttribute('for', id);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Handlers
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Properly update the value when the checkbox is (un)ticked to trigger
|
||||
* possible onchanges.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onChange(ev) {
|
||||
this._setValue(ev.target.checked);
|
||||
}
|
||||
/**
|
||||
* Implement keyboard movements. Mostly useful for its environment, such
|
||||
* as a list view.
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
* @param {KeyEvent} ev
|
||||
*/
|
||||
_onKeydown(ev) {
|
||||
switch (ev.which) {
|
||||
case $.ui.keyCode.ENTER:
|
||||
// prevent subsequent 'click' event (see _onKeydown of AbstractField)
|
||||
ev.preventDefault();
|
||||
this._setValue(!this.value);
|
||||
return;
|
||||
case $.ui.keyCode.UP:
|
||||
case $.ui.keyCode.RIGHT:
|
||||
case $.ui.keyCode.DOWN:
|
||||
case $.ui.keyCode.LEFT:
|
||||
ev.preventDefault();
|
||||
}
|
||||
super._onKeydown(ev);
|
||||
}
|
||||
}
|
||||
FieldBoolean.components = { CustomCheckbox };
|
||||
FieldBoolean.description = _lt("Checkbox");
|
||||
FieldBoolean.supportedFieldTypes = ['boolean'];
|
||||
FieldBoolean.template = 'web.FieldBoolean';
|
||||
|
||||
return {
|
||||
FieldBoolean,
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="web.FieldBoolean" owl="1">
|
||||
<CustomCheckbox disabled="mode === 'readonly'" value="value"
|
||||
class="o_field_boolean" t-on-change="_onChange"/>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -267,12 +267,14 @@ QUnit.module('basic_fields', {
|
||||
assert.containsOnce(form, '.o_field_boolean input:checked',
|
||||
"checkbox should now be checked");
|
||||
// blindly press enter again, it should uncheck the checkbox
|
||||
$(document.activeElement).trigger({type: "keydown", which: $.ui.keyCode.ENTER});
|
||||
await testUtils.dom.triggerEvent(document.activeElement, "keydown",
|
||||
{which: $.ui.keyCode.ENTER});
|
||||
assert.containsNone(form, '.o_field_boolean input:checked',
|
||||
"checkbox should not be checked");
|
||||
await testUtils.nextTick();
|
||||
// blindly press enter again, it should check the checkbox back
|
||||
$(document.activeElement).trigger({type: "keydown", which: $.ui.keyCode.ENTER});
|
||||
await testUtils.dom.triggerEvent(document.activeElement, "keydown",
|
||||
{which: $.ui.keyCode.ENTER});
|
||||
assert.containsOnce(form, '.o_field_boolean input:checked',
|
||||
"checkbox should still be checked");
|
||||
|
||||
@@ -333,6 +335,7 @@ QUnit.module('basic_fields', {
|
||||
// the checkbox directly in readonly mode !
|
||||
$cell = list.$('tr.o_data_row:has(.custom-checkbox input:not(:checked)) td:not(.o_list_record_selector)').first();
|
||||
await testUtils.dom.click($cell.find('.custom-checkbox .custom-control-label'));
|
||||
await testUtils.nextTick();
|
||||
|
||||
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector) .custom-checkbox input').length, 5,
|
||||
"should still have 5 checkboxes");
|
||||
|
||||
@@ -290,6 +290,7 @@
|
||||
<script type="text/javascript" src="/web/static/src/js/components/pager.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/apps.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/web/static/src/js/_deprecated/basic_fields.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/fields/abstract_field.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/fields/basic_fields.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/fields/field_registry.js"></script>
|
||||
@@ -301,6 +302,7 @@
|
||||
<script type="text/javascript" src="/web/static/src/js/fields/upgrade_fields.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/fields/field_wrapper.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/fields/abstract_field_owl.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/fields/basic_fields_owl.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/web/static/src/js/views/abstract_view.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/views/abstract_renderer.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user