[REF] web: update widget to new guidelines
- make delegate/undelegate events private - remove $el before start - make replaceElement private - remove one _ from __render_and_insert... method - update documentation - remove make and improve make_descriptive (also, it is private) - ...
This commit is contained in:
@@ -794,10 +794,10 @@ var ManualLineRenderer = LineRenderer.extend({
|
|||||||
|
|
||||||
return $.when.apply($, defs).then(function () {
|
return $.when.apply($, defs).then(function () {
|
||||||
if (!self.fields.title_account_id) {
|
if (!self.fields.title_account_id) {
|
||||||
self.fields.partner_id.$el.prependTo(self.$('.accounting_view thead td:eq(1) span:first'));
|
return self.fields.partner_id.prependTo(self.$('.accounting_view thead td:eq(1) span:first'));
|
||||||
} else {
|
} else {
|
||||||
self.fields.partner_id.destroy();
|
self.fields.partner_id.destroy();
|
||||||
self.fields.title_account_id.appendTo(self.$('.accounting_view thead td:eq(1) span:first'));
|
return self.fields.title_account_id.appendTo(self.$('.accounting_view thead td:eq(1) span:first'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -607,7 +607,6 @@ var Chrome = PosBaseWidget.extend({
|
|||||||
$(window).off();
|
$(window).off();
|
||||||
$('html').off();
|
$('html').off();
|
||||||
$('body').off();
|
$('body').off();
|
||||||
this.$el.parent().off();
|
|
||||||
// The above lines removed the bindings, but we really need them for the barcode
|
// The above lines removed the bindings, but we really need them for the barcode
|
||||||
BarcodeEvents.start();
|
BarcodeEvents.start();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ var PosBaseWidget = Widget.extend({
|
|||||||
this.pos = options.pos || (parent ? parent.pos : undefined);
|
this.pos = options.pos || (parent ? parent.pos : undefined);
|
||||||
this.chrome = options.chrome || (parent ? parent.chrome : undefined);
|
this.chrome = options.chrome || (parent ? parent.chrome : undefined);
|
||||||
this.gui = options.gui || (parent ? parent.gui : undefined);
|
this.gui = options.gui || (parent ? parent.gui : undefined);
|
||||||
|
|
||||||
|
// the widget class does not support anymore using $el/el before the
|
||||||
|
// 'start' lifecycle method, but point of sale actually needs it.
|
||||||
|
this.setElement(this._makeDescriptive());
|
||||||
},
|
},
|
||||||
format_currency: function(amount,precision){
|
format_currency: function(amount,precision){
|
||||||
var currency = (this.pos && this.pos.currency) ? this.pos.currency : {symbol:'$', position: 'after', rounding: 0.01, decimals: 2};
|
var currency = (this.pos && this.pos.currency) ? this.pos.currency : {symbol:'$', position: 'after', rounding: 0.01, decimals: 2};
|
||||||
|
|||||||
@@ -540,7 +540,7 @@ var FilterGroup = Input.extend(/** @lends instance.web.search.FilterGroup# */{
|
|||||||
*/
|
*/
|
||||||
search_change: function () {
|
search_change: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var $filters = this.$el.removeClass('selected');
|
var $filters = this.$el ? this.$el.removeClass('selected') : $();
|
||||||
var facet = this.searchview.query.find(_.bind(this.match_facet, this));
|
var facet = this.searchview.query.find(_.bind(this.match_facet, this));
|
||||||
if (!facet) { return; }
|
if (!facet) { return; }
|
||||||
facet.values.each(function (v) {
|
facet.values.each(function (v) {
|
||||||
|
|||||||
@@ -21,20 +21,26 @@ var ServicesMixin = require('web.ServicesMixin');
|
|||||||
*
|
*
|
||||||
* Here is a sample child class::
|
* Here is a sample child class::
|
||||||
*
|
*
|
||||||
* var MyWidget = openerp.base.Widget.extend({
|
* var MyWidget = Widget.extend({
|
||||||
* // the name of the QWeb template to use for rendering
|
* // the name of the QWeb template to use for rendering
|
||||||
* template: "MyQWebTemplate",
|
* template: "MyQWebTemplate",
|
||||||
*
|
*
|
||||||
* init: function(parent) {
|
* init: function (parent) {
|
||||||
* this._super(parent);
|
* this._super(parent);
|
||||||
* // stuff that you want to init before the rendering
|
* // stuff that you want to init before the rendering
|
||||||
* },
|
* },
|
||||||
|
* willStart: function () {
|
||||||
|
* // async work that need to be done before the widget is ready
|
||||||
|
* // this method should return a deferred
|
||||||
|
* },
|
||||||
* start: function() {
|
* start: function() {
|
||||||
* // stuff you want to make after the rendering, `this.$el` holds a correct value
|
* // stuff you want to make after the rendering, `this.$el` holds a correct value
|
||||||
* this.$(".my_button").click(/* an example of event binding * /);
|
* this.$(".my_button").click(/* an example of event binding * /);
|
||||||
*
|
*
|
||||||
* // if you have some asynchronous operations, it's a good idea to return
|
* // if you have some asynchronous operations, it's a good idea to return
|
||||||
* // a promise in start()
|
* // a promise in start(). Note that this is quite rare, and if you
|
||||||
|
* // need to fetch some data, this should probably be done in the
|
||||||
|
* // willStart method
|
||||||
* var promise = this._rpc(...);
|
* var promise = this._rpc(...);
|
||||||
* return promise;
|
* return promise;
|
||||||
* }
|
* }
|
||||||
@@ -42,8 +48,8 @@ var ServicesMixin = require('web.ServicesMixin');
|
|||||||
*
|
*
|
||||||
* Now this class can simply be used with the following syntax::
|
* Now this class can simply be used with the following syntax::
|
||||||
*
|
*
|
||||||
* var my_widget = new MyWidget(this);
|
* var myWidget = new MyWidget(this);
|
||||||
* my_widget.appendTo($(".some-div"));
|
* myWidget.appendTo($(".some-div"));
|
||||||
*
|
*
|
||||||
* With these two lines, the MyWidget instance was initialized, rendered,
|
* With these two lines, the MyWidget instance was initialized, rendered,
|
||||||
* inserted into the DOM inside the ``.some-div`` div and its events were
|
* inserted into the DOM inside the ``.some-div`` div and its events were
|
||||||
@@ -51,12 +57,11 @@ var ServicesMixin = require('web.ServicesMixin');
|
|||||||
*
|
*
|
||||||
* And of course, when you don't need that widget anymore, just do::
|
* And of course, when you don't need that widget anymore, just do::
|
||||||
*
|
*
|
||||||
* my_widget.destroy();
|
* myWidget.destroy();
|
||||||
*
|
*
|
||||||
* That will kill the widget in a clean way and erase its content from the dom.
|
* That will kill the widget in a clean way and erase its content from the dom.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
||||||
// Backbone-ish API
|
// Backbone-ish API
|
||||||
tagName: 'div',
|
tagName: 'div',
|
||||||
@@ -68,7 +73,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
* The name of the QWeb template that will be used for rendering. Must be
|
* The name of the QWeb template that will be used for rendering. Must be
|
||||||
* redefined in subclasses or the default render() method can not be used.
|
* redefined in subclasses or the default render() method can not be used.
|
||||||
*
|
*
|
||||||
* @type {String}
|
* @type {null|string}
|
||||||
*/
|
*/
|
||||||
template: null,
|
template: null,
|
||||||
/**
|
/**
|
||||||
@@ -76,16 +81,16 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
* be rendered. This will not induce loading anything that has already been
|
* be rendered. This will not induce loading anything that has already been
|
||||||
* loaded.
|
* loaded.
|
||||||
*
|
*
|
||||||
* @type {string[]}
|
* @type {null|string[]}
|
||||||
*/
|
*/
|
||||||
xmlDependencies: null,
|
xmlDependencies: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the widget and sets its parent if a parent is given.
|
* Constructs the widget and sets its parent if a parent is given.
|
||||||
*
|
*
|
||||||
* @param {openerp.Widget} parent Binds the current instance to the given Widget instance.
|
* @param {Widget|null} parent Binds the current instance to the given Widget
|
||||||
* When that widget is destroyed by calling destroy(), the current instance will be
|
* instance. When that widget is destroyed by calling destroy(), the
|
||||||
* destroyed too. Can be null.
|
* current instance will be destroyed too. Can be null.
|
||||||
*/
|
*/
|
||||||
init: function (parent) {
|
init: function (parent) {
|
||||||
mixins.PropertiesMixin.init.call(this);
|
mixins.PropertiesMixin.init.call(this);
|
||||||
@@ -93,14 +98,12 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
// Bind on_/do_* methods to this
|
// Bind on_/do_* methods to this
|
||||||
// We might remove this automatic binding in the future
|
// We might remove this automatic binding in the future
|
||||||
for (var name in this) {
|
for (var name in this) {
|
||||||
if(typeof(this[name]) == "function") {
|
if(typeof(this[name]) === "function") {
|
||||||
if((/^on_|^do_/).test(name)) {
|
if((/^on_|^do_/).test(name)) {
|
||||||
this[name] = _.bind(this[name], this);
|
this[name] = this[name].bind(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// FIXME: this should not be
|
|
||||||
this.setElement(this._make_descriptive());
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Method called between @see init and @see start. Performs asynchronous
|
* Method called between @see init and @see start. Performs asynchronous
|
||||||
@@ -121,65 +124,51 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
return $.when();
|
return $.when();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Destroys the current widget, also destroys all its children before destroying itself.
|
* Method called after rendering. Mostly used to bind actions, perform
|
||||||
|
* asynchronous calls, etc...
|
||||||
|
*
|
||||||
|
* By convention, this method should return an object that can be passed to
|
||||||
|
* $.when() to inform the caller when this widget has been initialized.
|
||||||
|
*
|
||||||
|
* Note that, for historic reasons, many widgets still do work in the start
|
||||||
|
* method that would be more suited to the willStart method.
|
||||||
|
*
|
||||||
|
* @returns {Deferred}
|
||||||
|
*/
|
||||||
|
start: function () {
|
||||||
|
return $.when();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Destroys the current widget, also destroys all its children before
|
||||||
|
* destroying itself.
|
||||||
*/
|
*/
|
||||||
destroy: function () {
|
destroy: function () {
|
||||||
_.each(this.getChildren(), function (el) {
|
_.invoke(this.getChildren(), 'destroy');
|
||||||
el.destroy();
|
|
||||||
});
|
|
||||||
if(this.$el) {
|
if(this.$el) {
|
||||||
this.$el.remove();
|
this.$el.remove();
|
||||||
}
|
}
|
||||||
mixins.PropertiesMixin.destroy.call(this);
|
mixins.PropertiesMixin.destroy.call(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Public
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the current widget and appends it to the given jQuery object or Widget.
|
* Renders the current widget and appends it to the given jQuery object.
|
||||||
*
|
*
|
||||||
* @param target A jQuery object or a Widget instance.
|
* @param {jQuery} target
|
||||||
*/
|
*/
|
||||||
appendTo: function (target) {
|
appendTo: function (target) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this.__widgetRenderAndInsert(function (t) {
|
return this._widgetRenderAndInsert(function (t) {
|
||||||
self.$el.appendTo(t);
|
self.$el.appendTo(t);
|
||||||
}, target);
|
}, target);
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* Renders the current widget and prepends it to the given jQuery object or Widget.
|
|
||||||
*
|
|
||||||
* @param target A jQuery object or a Widget instance.
|
|
||||||
*/
|
|
||||||
prependTo: function (target) {
|
|
||||||
var self = this;
|
|
||||||
return this.__widgetRenderAndInsert(function (t) {
|
|
||||||
self.$el.prependTo(t);
|
|
||||||
}, target);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Renders the current widget and inserts it after to the given jQuery object or Widget.
|
|
||||||
*
|
|
||||||
* @param target A jQuery object or a Widget instance.
|
|
||||||
*/
|
|
||||||
insertAfter: function (target) {
|
|
||||||
var self = this;
|
|
||||||
return this.__widgetRenderAndInsert(function (t) {
|
|
||||||
self.$el.insertAfter(t);
|
|
||||||
}, target);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Renders the current widget and inserts it before to the given jQuery object or Widget.
|
|
||||||
*
|
|
||||||
* @param target A jQuery object or a Widget instance.
|
|
||||||
*/
|
|
||||||
insertBefore: function (target) {
|
|
||||||
var self = this;
|
|
||||||
return this.__widgetRenderAndInsert(function (t) {
|
|
||||||
self.$el.insertBefore(t);
|
|
||||||
}, target);
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* Attach the current widget to a dom element
|
* Attach the current widget to a dom element
|
||||||
*
|
*
|
||||||
* @param target A jQuery object or a Widget instance.
|
* @param {jQuery} target
|
||||||
*/
|
*/
|
||||||
attachTo: function (target) {
|
attachTo: function (target) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -189,68 +178,86 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Renders the current widget and replaces the given jQuery object.
|
* Hides the widget
|
||||||
*
|
|
||||||
* @param target A jQuery object or a Widget instance.
|
|
||||||
*/
|
*/
|
||||||
replace: function (target) {
|
do_hide: function () {
|
||||||
return this.__widgetRenderAndInsert(_.bind(function (t) {
|
this.$el.addClass('o_hidden');
|
||||||
this.$el.replaceAll(t);
|
|
||||||
}, this), target);
|
|
||||||
},
|
},
|
||||||
__widgetRenderAndInsert: function (insertion, target) {
|
/**
|
||||||
|
* Displays the widget
|
||||||
|
*/
|
||||||
|
do_show: function () {
|
||||||
|
this.$el.removeClass('o_hidden');
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Displays or hides the widget
|
||||||
|
* @param {boolean} [display] use true to show the widget or false to hide it
|
||||||
|
*/
|
||||||
|
do_toggle: function (display) {
|
||||||
|
if (_.isBoolean(display)) {
|
||||||
|
display ? this.do_show() : this.do_hide();
|
||||||
|
} else {
|
||||||
|
this.$el.hasClass('o_hidden') ? this.do_show() : this.do_hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Renders the current widget and inserts it after to the given jQuery
|
||||||
|
* object.
|
||||||
|
*
|
||||||
|
* @param {jQuery} target
|
||||||
|
*/
|
||||||
|
insertAfter: function (target) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this.willStart().then(function () {
|
return this._widgetRenderAndInsert(function (t) {
|
||||||
self.renderElement();
|
self.$el.insertAfter(t);
|
||||||
insertion(target);
|
}, target);
|
||||||
return self.start();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Method called after rendering. Mostly used to bind actions, perform asynchronous
|
* Renders the current widget and inserts it before to the given jQuery
|
||||||
* calls, etc...
|
* object.
|
||||||
*
|
*
|
||||||
* By convention, this method should return an object that can be passed to $.when()
|
* @param {jQuery} target
|
||||||
* to inform the caller when this widget has been initialized.
|
|
||||||
*
|
|
||||||
* @returns {jQuery.Deferred or any}
|
|
||||||
*/
|
*/
|
||||||
start: function () {
|
insertBefore: function (target) {
|
||||||
return $.when();
|
var self = this;
|
||||||
|
return this._widgetRenderAndInsert(function (t) {
|
||||||
|
self.$el.insertBefore(t);
|
||||||
|
}, target);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Renders the element. The default implementation renders the widget using QWeb,
|
* Renders the current widget and prepends it to the given jQuery object.
|
||||||
* `this.template` must be defined. The context given to QWeb contains the "widget"
|
*
|
||||||
* key that references `this`.
|
* @param {jQuery} target
|
||||||
|
*/
|
||||||
|
prependTo: function (target) {
|
||||||
|
var self = this;
|
||||||
|
return this._widgetRenderAndInsert(function (t) {
|
||||||
|
self.$el.prependTo(t);
|
||||||
|
}, target);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Renders the element. The default implementation renders the widget using
|
||||||
|
* QWeb, `this.template` must be defined. The context given to QWeb contains
|
||||||
|
* the "widget" key that references `this`.
|
||||||
*/
|
*/
|
||||||
renderElement: function () {
|
renderElement: function () {
|
||||||
var $el;
|
var $el;
|
||||||
if (this.template) {
|
if (this.template) {
|
||||||
$el = $(core.qweb.render(this.template, {widget: this}).trim());
|
$el = $(core.qweb.render(this.template, {widget: this}).trim());
|
||||||
} else {
|
} else {
|
||||||
$el = this._make_descriptive();
|
$el = this._makeDescriptive();
|
||||||
}
|
}
|
||||||
this.replaceElement($el);
|
this._replaceElement($el);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Re-sets the widget's root element and replaces the old root element
|
* Renders the current widget and replaces the given jQuery object.
|
||||||
* (if any) by the new one in the DOM.
|
|
||||||
*
|
*
|
||||||
* @param {HTMLElement | jQuery} $el
|
* @param target A jQuery object or a Widget instance.
|
||||||
* @returns {Widget} this
|
|
||||||
*/
|
*/
|
||||||
replaceElement: function ($el) {
|
replace: function (target) {
|
||||||
var $oldel = this.$el;
|
return this._widgetRenderAndInsert(_.bind(function (t) {
|
||||||
this.setElement($el);
|
this.$el.replaceAll(t);
|
||||||
if ($oldel && !$oldel.is(this.$el)) {
|
}, this), target);
|
||||||
if ($oldel.length > 1) {
|
|
||||||
$oldel.wrapAll('<div/>');
|
|
||||||
$oldel.parent().replaceWith(this.$el);
|
|
||||||
} else {
|
|
||||||
$oldel.replaceWith(this.$el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Re-sets the widget's root element (el/$el/$el).
|
* Re-sets the widget's root element (el/$el/$el).
|
||||||
@@ -266,51 +273,41 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
* @return {Widget} this
|
* @return {Widget} this
|
||||||
*/
|
*/
|
||||||
setElement: function (element) {
|
setElement: function (element) {
|
||||||
// NB: completely useless, as WidgetMixin#init creates a $el
|
|
||||||
// always
|
|
||||||
if (this.$el) {
|
if (this.$el) {
|
||||||
this.undelegateEvents();
|
this._undelegateEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$el = (element instanceof $) ? element : $(element);
|
this.$el = (element instanceof $) ? element : $(element);
|
||||||
this.el = this.$el[0];
|
this.el = this.$el[0];
|
||||||
|
|
||||||
this.delegateEvents();
|
this._delegateEvents();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Private
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility function to build small DOM elements.
|
* Helper method, for ``this.$el.find(selector)``
|
||||||
*
|
*
|
||||||
* @param {String} tagName name of the DOM element to create
|
* @private
|
||||||
* @param {Object} [attributes] map of DOM attributes to set on the element
|
* @param {string} selector CSS selector, rooted in $el
|
||||||
* @param {String} [content] HTML content to set on the element
|
* @returns {jQuery} selector match
|
||||||
* @return {Element}
|
|
||||||
*/
|
*/
|
||||||
make: function (tagName, attributes, content) {
|
$: function (selector) {
|
||||||
var el = document.createElement(tagName);
|
if (selector === undefined) {
|
||||||
if (!_.isEmpty(attributes)) {
|
return this.$el;
|
||||||
$(el).attr(attributes);
|
|
||||||
}
|
}
|
||||||
if (content) {
|
return this.$el.find(selector);
|
||||||
$(el).html(content);
|
|
||||||
}
|
|
||||||
return el;
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Makes a potential root element from the declarative builder of the
|
* Attach event handlers for events described in the 'events' key
|
||||||
* widget
|
|
||||||
*
|
*
|
||||||
* @return {jQuery}
|
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_make_descriptive: function () {
|
_delegateEvents: function () {
|
||||||
var attrs = _.extend({}, this.attributes || {});
|
|
||||||
if (this.id) { attrs.id = this.id; }
|
|
||||||
if (this.className) { attrs['class'] = this.className; }
|
|
||||||
return $(this.make(this.tagName, attrs));
|
|
||||||
},
|
|
||||||
delegateEvents: function () {
|
|
||||||
var events = this.events;
|
var events = this.events;
|
||||||
if (_.isEmpty(events)) { return; }
|
if (_.isEmpty(events)) { return; }
|
||||||
|
|
||||||
@@ -331,42 +328,73 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undelegateEvents: function () {
|
/**
|
||||||
|
* Makes a potential root element from the declarative builder of the
|
||||||
|
* widget
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @return {jQuery}
|
||||||
|
*/
|
||||||
|
_makeDescriptive: function () {
|
||||||
|
var attrs = _.extend({}, this.attributes || {});
|
||||||
|
if (this.id) {
|
||||||
|
attrs.id = this.id;
|
||||||
|
}
|
||||||
|
if (this.className) {
|
||||||
|
attrs['class'] = this.className;
|
||||||
|
}
|
||||||
|
var $el = $(document.createElement(this.tagName));
|
||||||
|
if (!_.isEmpty(attrs)) {
|
||||||
|
$el.attr(attrs);
|
||||||
|
}
|
||||||
|
return $el;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Re-sets the widget's root element and replaces the old root element
|
||||||
|
* (if any) by the new one in the DOM.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {HTMLElement | jQuery} $el
|
||||||
|
* @returns {Widget} this instance, so it can be chained
|
||||||
|
*/
|
||||||
|
_replaceElement: function ($el) {
|
||||||
|
var $oldel = this.$el;
|
||||||
|
this.setElement($el);
|
||||||
|
if ($oldel && !$oldel.is(this.$el)) {
|
||||||
|
if ($oldel.length > 1) {
|
||||||
|
$oldel.wrapAll('<div/>');
|
||||||
|
$oldel.parent().replaceWith(this.$el);
|
||||||
|
} else {
|
||||||
|
$oldel.replaceWith(this.$el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Remove all handlers registered on this.$el
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_undelegateEvents: function () {
|
||||||
this.$el.off('.widget_events');
|
this.$el.off('.widget_events');
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Shortcut for ``this.$el.find(selector)``
|
* Render the widget. This is a private method, and should really never be
|
||||||
|
* called by anyone (except this widget). It assumes that the widget was
|
||||||
|
* not willStarted yet.
|
||||||
*
|
*
|
||||||
* @param {String} selector CSS selector, rooted in $el
|
* @private
|
||||||
* @returns {jQuery} selector match
|
* @param {function: jQuery -> any} insertion
|
||||||
|
* @param {jQuery} target
|
||||||
|
* @returns {Deferred}
|
||||||
*/
|
*/
|
||||||
$: function (selector) {
|
_widgetRenderAndInsert: function (insertion, target) {
|
||||||
if (selector === undefined)
|
var self = this;
|
||||||
return this.$el;
|
return this.willStart().then(function () {
|
||||||
return this.$el.find(selector);
|
self.renderElement();
|
||||||
},
|
insertion(target);
|
||||||
/**
|
return self.start();
|
||||||
* Displays the widget
|
});
|
||||||
*/
|
|
||||||
do_show: function () {
|
|
||||||
this.$el.removeClass('o_hidden');
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Hides the widget
|
|
||||||
*/
|
|
||||||
do_hide: function () {
|
|
||||||
this.$el.addClass('o_hidden');
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Displays or hides the widget
|
|
||||||
* @param {Boolean} [display] use true to show the widget or false to hide it
|
|
||||||
*/
|
|
||||||
do_toggle: function (display) {
|
|
||||||
if (_.isBoolean(display)) {
|
|
||||||
display ? this.do_show() : this.do_hide();
|
|
||||||
} else {
|
|
||||||
this.$el.hasClass('o_hidden') ? this.do_show() : this.do_hide();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -438,7 +438,7 @@ var FieldDate = InputField.extend({
|
|||||||
def = this.datewidget.appendTo('<div>').done(function () {
|
def = this.datewidget.appendTo('<div>').done(function () {
|
||||||
self.datewidget.$el.addClass(self.$el.attr('class'));
|
self.datewidget.$el.addClass(self.$el.attr('class'));
|
||||||
self._prepareInput(self.datewidget.$input);
|
self._prepareInput(self.datewidget.$input);
|
||||||
self.replaceElement(self.datewidget.$el);
|
self._replaceElement(self.datewidget.$el);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return $.when(def, this._super.apply(this, arguments));
|
return $.when(def, this._super.apply(this, arguments));
|
||||||
|
|||||||
@@ -483,15 +483,18 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
* rendering of the widget will be started and the associated deferred will
|
* rendering of the widget will be started and the associated deferred will
|
||||||
* be added to the 'defs' attribute. This is supposed to be created and
|
* be added to the 'defs' attribute. This is supposed to be created and
|
||||||
* deleted by the calling code if necessary.
|
* deleted by the calling code if necessary.
|
||||||
* Note: for this implementation to work, AbstractField willStart methods
|
*
|
||||||
* *must* be synchronous.
|
* Note: we always return a $el. If the field widget is asynchronous, this
|
||||||
|
* $el will be replaced by the real $el, whenever the widget is ready (start
|
||||||
|
* method is done). This means that this is not the correct place to make
|
||||||
|
* changes on the widget $el. For this, @see _postProcessField method
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} node
|
* @param {Object} node
|
||||||
* @param {Object} record
|
* @param {Object} record
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @param {Object} [modifiersOptions]
|
* @param {Object} [modifiersOptions]
|
||||||
* @returns {AbstractField}
|
* @returns {jQueryElement}
|
||||||
*/
|
*/
|
||||||
_renderFieldWidget: function (node, record, options, modifiersOptions) {
|
_renderFieldWidget: function (node, record, options, modifiersOptions) {
|
||||||
var fieldName = node.attrs.name;
|
var fieldName = node.attrs.name;
|
||||||
@@ -516,8 +519,10 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
widget.__node = node; // TODO get rid of this if possible one day
|
widget.__node = node; // TODO get rid of this if possible one day
|
||||||
|
|
||||||
// Prepare widget rendering and save the related deferred
|
// Prepare widget rendering and save the related deferred
|
||||||
var def = widget.__widgetRenderAndInsert(function () {});
|
var def = widget._widgetRenderAndInsert(function () {});
|
||||||
if (def.state() === 'pending') {
|
var async = def.state() === 'pending';
|
||||||
|
var $el = async ? $('<div>') : widget.$el;
|
||||||
|
if (async) {
|
||||||
this.defs.push(def);
|
this.defs.push(def);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,6 +531,9 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
// associated to new widget)
|
// associated to new widget)
|
||||||
var self = this;
|
var self = this;
|
||||||
def.then(function () {
|
def.then(function () {
|
||||||
|
if (async) {
|
||||||
|
$el.replaceWith(widget.$el);
|
||||||
|
}
|
||||||
self._registerModifiers(node, record, widget, _.extend({
|
self._registerModifiers(node, record, widget, _.extend({
|
||||||
callback: function (element, modifiers, record) {
|
callback: function (element, modifiers, record) {
|
||||||
element.$el.toggleClass('o_field_empty', !!(
|
element.$el.toggleClass('o_field_empty', !!(
|
||||||
@@ -538,7 +546,7 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
self._postProcessField(widget, node);
|
self._postProcessField(widget, node);
|
||||||
});
|
});
|
||||||
|
|
||||||
return widget;
|
return $el;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Renders the nocontent helper.
|
* Renders the nocontent helper.
|
||||||
@@ -583,7 +591,7 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
var widget = new Widget(this, record);
|
var widget = new Widget(this, record);
|
||||||
|
|
||||||
// Prepare widget rendering and save the related deferred
|
// Prepare widget rendering and save the related deferred
|
||||||
var def = widget.__widgetRenderAndInsert(function () {});
|
var def = widget._widgetRenderAndInsert(function () {});
|
||||||
if (def.state() === 'pending') {
|
if (def.state() === 'pending') {
|
||||||
this.defs.push(def);
|
this.defs.push(def);
|
||||||
}
|
}
|
||||||
@@ -602,20 +610,17 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @param {Widget} widget
|
* @param {Widget} widget
|
||||||
* @param {Object} record
|
* @param {Object} record
|
||||||
* @returns {AbstractField}
|
|
||||||
*/
|
*/
|
||||||
_rerenderFieldWidget: function (widget, record) {
|
_rerenderFieldWidget: function (widget, record) {
|
||||||
// Render the new field widget
|
// Render the new field widget
|
||||||
var newWidget = this._renderFieldWidget(widget.__node, record);
|
var $el = this._renderFieldWidget(widget.__node, record);
|
||||||
widget.$el.replaceWith(newWidget.$el);
|
widget.$el.replaceWith($el);
|
||||||
|
|
||||||
// Destroy the old widget and position the new one at the old one's
|
// Destroy the old widget and position the new one at the old one's
|
||||||
var oldIndex = this._destroyFieldWidget(record.id, widget);
|
var oldIndex = this._destroyFieldWidget(record.id, widget);
|
||||||
var recordWidgets = this.allFieldWidgets[record.id];
|
var recordWidgets = this.allFieldWidgets[record.id];
|
||||||
|
var newWidget = recordWidgets.pop();
|
||||||
recordWidgets.splice(oldIndex, 0, newWidget);
|
recordWidgets.splice(oldIndex, 0, newWidget);
|
||||||
recordWidgets.pop();
|
|
||||||
|
|
||||||
return newWidget;
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Unregisters an element of the modifiers data associated to the given
|
* Unregisters an element of the modifiers data associated to the given
|
||||||
|
|||||||
@@ -443,8 +443,8 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
* @returns {jQueryElement}
|
* @returns {jQueryElement}
|
||||||
*/
|
*/
|
||||||
_renderInnerGroupField: function (node) {
|
_renderInnerGroupField: function (node) {
|
||||||
var widget = this._renderFieldWidget(node, this.state);
|
var $el = this._renderFieldWidget(node, this.state);
|
||||||
var $tds = $('<td/>').append(widget.$el);
|
var $tds = $('<td/>').append($el);
|
||||||
|
|
||||||
if (node.attrs.nolabel !== '1') {
|
if (node.attrs.nolabel !== '1') {
|
||||||
var $labelTd = this._renderInnerGroupLabel(node);
|
var $labelTd = this._renderInnerGroupLabel(node);
|
||||||
@@ -570,7 +570,7 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
* @returns {jQueryElement}
|
* @returns {jQueryElement}
|
||||||
*/
|
*/
|
||||||
_renderTagField: function (node) {
|
_renderTagField: function (node) {
|
||||||
return this._renderFieldWidget(node, this.state).$el;
|
return this._renderFieldWidget(node, this.state);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -624,8 +624,8 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
$statusbar.append(this._renderHeaderButtons(node));
|
$statusbar.append(this._renderHeaderButtons(node));
|
||||||
_.each(node.children, function (child) {
|
_.each(node.children, function (child) {
|
||||||
if (child.tag === 'field') {
|
if (child.tag === 'field') {
|
||||||
var widget = self._renderFieldWidget(child, self.state);
|
var $el = self._renderFieldWidget(child, self.state);
|
||||||
$statusbar.append(widget.$el);
|
$statusbar.append($el);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this._handleAttributes($statusbar, node);
|
this._handleAttributes($statusbar, node);
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ return AbstractRenderer.extend({
|
|||||||
init: function (parent, state, params) {
|
init: function (parent, state, params) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
this.stacked = params.stacked;
|
this.stacked = params.stacked;
|
||||||
this.$el.css({minWidth: '100px', minHeight: '100px'});
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ var KanbanRecord = Widget.extend({
|
|||||||
var Widget = widgetRegistry.get($field.attr('name'));
|
var Widget = widgetRegistry.get($field.attr('name'));
|
||||||
var widget = new Widget(self, self.state);
|
var widget = new Widget(self, self.state);
|
||||||
|
|
||||||
var def = widget.__widgetRenderAndInsert(function () {});
|
var def = widget._widgetRenderAndInsert(function () {});
|
||||||
if (def.state() === 'pending') {
|
if (def.state() === 'pending') {
|
||||||
self.defs.push(def);
|
self.defs.push(def);
|
||||||
}
|
}
|
||||||
@@ -282,7 +282,7 @@ var KanbanRecord = Widget.extend({
|
|||||||
* Renders the record
|
* Renders the record
|
||||||
*/
|
*/
|
||||||
_render: function () {
|
_render: function () {
|
||||||
this.replaceElement(this.qweb.render('kanban-box', this.qweb_context));
|
this._replaceElement(this.qweb.render('kanban-box', this.qweb_context));
|
||||||
this.$el.addClass('o_kanban_record');
|
this.$el.addClass('o_kanban_record');
|
||||||
this.$el.data('record', this);
|
this.$el.data('record', this);
|
||||||
if (this.$el.hasClass('oe_kanban_global_click') ||
|
if (this.$el.hasClass('oe_kanban_global_click') ||
|
||||||
|
|||||||
@@ -274,8 +274,8 @@ var ListRenderer = BasicRenderer.extend({
|
|||||||
return $td.append(this._renderWidget(record, node));
|
return $td.append(this._renderWidget(record, node));
|
||||||
}
|
}
|
||||||
if (node.attrs.widget || (options && options.renderWidgets)) {
|
if (node.attrs.widget || (options && options.renderWidgets)) {
|
||||||
var widget = this._renderFieldWidget(node, record, _.pick(options, 'mode'));
|
var $el = this._renderFieldWidget(node, record, _.pick(options, 'mode'));
|
||||||
return $td.append(widget.$el);
|
return $td.append($el);
|
||||||
}
|
}
|
||||||
var name = node.attrs.name;
|
var name = node.attrs.name;
|
||||||
var field = this.state.fields[name];
|
var field = this.state.fields[name];
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ var PivotRenderer = AbstractRenderer.extend({
|
|||||||
_render: function () {
|
_render: function () {
|
||||||
if (!this._hasContent()) {
|
if (!this._hasContent()) {
|
||||||
// display the nocontent helper
|
// display the nocontent helper
|
||||||
this.replaceElement(QWeb.render('PivotView.nodata'));
|
this._replaceElement(QWeb.render('PivotView.nodata'));
|
||||||
return this._super.apply(this, arguments);
|
return this._super.apply(this, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,17 +82,15 @@ QUnit.module('core', {}, function () {
|
|||||||
|
|
||||||
|
|
||||||
QUnit.test('renderElement, no template, default', function (assert) {
|
QUnit.test('renderElement, no template, default', function (assert) {
|
||||||
assert.expect(8);
|
assert.expect(7);
|
||||||
|
|
||||||
var widget = new (Widget.extend({ }))();
|
var widget = new (Widget.extend({ }))();
|
||||||
|
|
||||||
var $original = widget.$el;
|
assert.strictEqual(widget.$el, undefined, "should not have a root element");
|
||||||
assert.ok($original, "should initially have a root element");
|
|
||||||
|
|
||||||
widget.renderElement();
|
widget.renderElement();
|
||||||
|
|
||||||
assert.ok(widget.$el, "should have generated a root element");
|
assert.ok(widget.$el, "should have generated a root element");
|
||||||
assert.ok($original !== widget.$el, "should have generated a new root element");
|
|
||||||
assert.strictEqual(widget.$el, widget.$el, "should provide $el alias");
|
assert.strictEqual(widget.$el, widget.$el, "should provide $el alias");
|
||||||
assert.ok(widget.$el.is(widget.el), "should provide raw DOM alias");
|
assert.ok(widget.$el.is(widget.el), "should provide raw DOM alias");
|
||||||
|
|
||||||
@@ -306,7 +304,7 @@ QUnit.module('core', {}, function () {
|
|||||||
assert.ok(newclicked, "should trigger bound events");
|
assert.ok(newclicked, "should trigger bound events");
|
||||||
|
|
||||||
clicked = newclicked = false;
|
clicked = newclicked = false;
|
||||||
widget.undelegateEvents();
|
widget._undelegateEvents();
|
||||||
widget.$('li').click();
|
widget.$('li').click();
|
||||||
assert.ok(!clicked, "undelegate should unbind events delegated");
|
assert.ok(!clicked, "undelegate should unbind events delegated");
|
||||||
assert.ok(newclicked, "undelegate should only unbind events it created");
|
assert.ok(newclicked, "undelegate should only unbind events it created");
|
||||||
|
|||||||
Reference in New Issue
Block a user