[ADD] basic loading of default values in faceted search view
bzr revid: xmo@openerp.com-20120320102846-g2ymabvt4qc4bwzg
This commit is contained in:
parent
16fddf9207
commit
b39712cf3d
34
.bzrignore
34
.bzrignore
|
@ -1,23 +1,13 @@
|
||||||
*.pyc
|
.*
|
||||||
.*.swp
|
*.egg-info
|
||||||
.bzrignore
|
*.orig
|
||||||
openerp/addons/*
|
|
||||||
openerp/filestore*
|
|
||||||
.Python
|
|
||||||
include
|
|
||||||
lib
|
|
||||||
bin/activate
|
|
||||||
bin/activate_this.py
|
|
||||||
bin/easy_install
|
|
||||||
bin/easy_install-2.6
|
|
||||||
bin/pip
|
|
||||||
bin/python
|
|
||||||
bin/python2.6
|
|
||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
build/
|
build/
|
||||||
bin/yolk
|
RE:^bin/
|
||||||
bin/pil*.py
|
RE:^dist/
|
||||||
.project
|
RE:^include/
|
||||||
.pydevproject
|
|
||||||
.settings
|
RE:^share/
|
||||||
|
RE:^man/
|
||||||
|
RE:^lib/
|
||||||
|
|
||||||
|
RE:^doc/_build/
|
||||||
|
|
|
@ -314,6 +314,7 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
on_loaded: function(data) {
|
on_loaded: function(data) {
|
||||||
|
var self = this;
|
||||||
this.fields_view = data.fields_view;
|
this.fields_view = data.fields_view;
|
||||||
if (data.fields_view.type !== 'search' ||
|
if (data.fields_view.type !== 'search' ||
|
||||||
data.fields_view.arch.tag !== 'search') {
|
data.fields_view.arch.tag !== 'search') {
|
||||||
|
@ -322,12 +323,16 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search
|
||||||
data.fields_view.type, data.fields_view.arch.tag));
|
data.fields_view.type, data.fields_view.arch.tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this,
|
this.make_widgets(
|
||||||
lines = this.make_widgets(
|
|
||||||
data.fields_view['arch'].children,
|
data.fields_view['arch'].children,
|
||||||
data.fields_view.fields);
|
data.fields_view.fields);
|
||||||
|
|
||||||
return this.ready.resolve().promise();
|
// load defaults
|
||||||
|
return $.when.apply(null, _(this.inputs).invoke('facet_for_defaults', this.defaults))
|
||||||
|
.then(function () {
|
||||||
|
self.vs.searchQuery.reset(_(arguments).compact());
|
||||||
|
self.ready.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
// for extended search view
|
// for extended search view
|
||||||
var ext = new openerp.web.search.ExtendedSearch(this, this.model);
|
var ext = new openerp.web.search.ExtendedSearch(this, this.model);
|
||||||
|
@ -730,10 +735,6 @@ openerp.web.search.Widget = openerp.web.OldWidget.extend( /** @lends openerp.web
|
||||||
destroy: function () {
|
destroy: function () {
|
||||||
delete this.view;
|
delete this.view;
|
||||||
this._super();
|
this._super();
|
||||||
},
|
|
||||||
render: function (defaults) {
|
|
||||||
// FIXME
|
|
||||||
return this._super(_.extend(this, {defaults: defaults}));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
openerp.web.search.add_expand_listener = function($root) {
|
openerp.web.search.add_expand_listener = function($root) {
|
||||||
|
@ -781,11 +782,29 @@ openerp.web.search.Input = openerp.web.search.Widget.extend( /** @lends openerp.
|
||||||
* label, value prefixed with an object with keys type=section and label
|
* label, value prefixed with an object with keys type=section and label
|
||||||
*
|
*
|
||||||
* @param {String} value value to complete
|
* @param {String} value value to complete
|
||||||
* @returns {jQuery.Deferred<null|Object>}
|
* @returns {jQuery.Deferred<null|Array>}
|
||||||
*/
|
*/
|
||||||
complete: function (value) {
|
complete: function (value) {
|
||||||
return $.when(null)
|
return $.when(null)
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Returns a VS.model.SearchFacet instance for the provided defaults if
|
||||||
|
* they apply to this widget, or null if they don't.
|
||||||
|
*
|
||||||
|
* This default implementation will try calling
|
||||||
|
* :js:func:`openerp.web.search.Input#facet_for` if the widget's name
|
||||||
|
* matches the input key
|
||||||
|
*
|
||||||
|
* @param {Object} defaults
|
||||||
|
* @returns {jQuery.Deferred<null|Object>}
|
||||||
|
*/
|
||||||
|
facet_for_defaults: function (defaults) {
|
||||||
|
if (!this.attrs ||
|
||||||
|
!(this.attrs.name in defaults && defaults[this.attrs.name])) {
|
||||||
|
return $.when(null);
|
||||||
|
}
|
||||||
|
return this.facet_for(defaults[this.attrs.name]);
|
||||||
|
},
|
||||||
get_context: function () {
|
get_context: function () {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"get_context not implemented for widget " + this.attrs.type);
|
"get_context not implemented for widget " + this.attrs.type);
|
||||||
|
@ -879,26 +898,18 @@ openerp.web.search.Filter = openerp.web.search.Input.extend(/** @lends openerp.w
|
||||||
self.view.do_toggle_filter(self);
|
self.view.do_toggle_filter(self);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* Returns whether the filter is currently enabled (in use) or not.
|
|
||||||
*
|
|
||||||
* @returns a boolean
|
|
||||||
*/
|
|
||||||
is_enabled:function () {
|
|
||||||
return this.$element.hasClass('enabled');
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* If the filter is present in the defaults (and has a truthy value),
|
* If the filter is present in the defaults (and has a truthy value),
|
||||||
* enable the filter.
|
* enable the filter.
|
||||||
*
|
*
|
||||||
* @param {Object} defaults the search view's default values
|
* @param {Object} defaults the search view's default values
|
||||||
*/
|
*/
|
||||||
render: function (defaults) {
|
facet_for: function (value) {
|
||||||
if (this.attrs.name && defaults[this.attrs.name]) {
|
return $.when(new VS.model.SearchFacet({
|
||||||
this.classes.push('enabled');
|
category: this.attrs.string || this.attrs.name,
|
||||||
this.view.do_toggle_filter(this, true);
|
value: 'true',
|
||||||
}
|
app: this.view.vs
|
||||||
return this._super(defaults);
|
}));
|
||||||
},
|
},
|
||||||
get_context: function () {
|
get_context: function () {
|
||||||
if (!this.is_enabled()) {
|
if (!this.is_enabled()) {
|
||||||
|
@ -939,6 +950,13 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w
|
||||||
})), view);
|
})), view);
|
||||||
this.make_id('input', field.type, this.attrs.name);
|
this.make_id('input', field.type, this.attrs.name);
|
||||||
},
|
},
|
||||||
|
facet_for: function (value) {
|
||||||
|
return $.when(new VS.model.SearchFacet({
|
||||||
|
category: this.attrs.name,
|
||||||
|
value: String(value),
|
||||||
|
app: this.view.vs
|
||||||
|
}));
|
||||||
|
},
|
||||||
start: function () {
|
start: function () {
|
||||||
this._super();
|
this._super();
|
||||||
this.filters.start();
|
this.filters.start();
|
||||||
|
@ -1096,6 +1114,17 @@ openerp.web.search.SelectionField = openerp.web.search.Field.extend(/** @lends o
|
||||||
return $.when.apply(null,
|
return $.when.apply(null,
|
||||||
[{type: 'section', label: this.attrs.string}].concat(results));
|
[{type: 'section', label: this.attrs.string}].concat(results));
|
||||||
},
|
},
|
||||||
|
facet_for: function (value) {
|
||||||
|
var match = _(this.attrs.selection).detect(function (sel) {
|
||||||
|
return sel[0] === value;
|
||||||
|
});
|
||||||
|
if (!match) { return $.when(null); }
|
||||||
|
return $.when(new VS.model.SearchFacet({
|
||||||
|
category: this.attrs.name,
|
||||||
|
value: match[1],
|
||||||
|
app: this.view.vs
|
||||||
|
}));
|
||||||
|
},
|
||||||
get_value: function () {
|
get_value: function () {
|
||||||
var index = parseInt(this.$element.val(), 10);
|
var index = parseInt(this.$element.val(), 10);
|
||||||
if (isNaN(index)) { return null; }
|
if (isNaN(index)) { return null; }
|
||||||
|
@ -1103,20 +1132,6 @@ openerp.web.search.SelectionField = openerp.web.search.Field.extend(/** @lends o
|
||||||
if (value === false) { return null; }
|
if (value === false) { return null; }
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* The selection field needs a default ``false`` value in case none is
|
|
||||||
* provided, so that selector options with a ``false`` value (convention
|
|
||||||
* for explicitly empty options) get selected by default rather than the
|
|
||||||
* first (value-holding) option in the selection.
|
|
||||||
*
|
|
||||||
* @param {Object} defaults search default values
|
|
||||||
*/
|
|
||||||
render: function (defaults) {
|
|
||||||
if (!defaults[this.attrs.name]) {
|
|
||||||
defaults[this.attrs.name] = false;
|
|
||||||
}
|
|
||||||
return this._super(defaults);
|
|
||||||
},
|
|
||||||
clear: function () {
|
clear: function () {
|
||||||
var self = this, d = $.Deferred(), selection = this.attrs.selection;
|
var self = this, d = $.Deferred(), selection = this.attrs.selection;
|
||||||
for(var index=0; index<selection.length; ++index) {
|
for(var index=0; index<selection.length; ++index) {
|
||||||
|
@ -1146,23 +1161,6 @@ openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @
|
||||||
['false', _t("No")]
|
['false', _t("No")]
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* Search defaults likely to be boolean values (for a boolean field).
|
|
||||||
*
|
|
||||||
* In the HTML, we only want/get strings, and our strings here are ``true``
|
|
||||||
* and ``false``, so ensure we use precisely those by truth-testing the
|
|
||||||
* default value (iif there is one in the view's defaults).
|
|
||||||
*
|
|
||||||
* @param {Object} defaults default values for this search view
|
|
||||||
* @returns {String} rendered boolean field
|
|
||||||
*/
|
|
||||||
render: function (defaults) {
|
|
||||||
var name = this.attrs.name;
|
|
||||||
if (name in defaults) {
|
|
||||||
defaults[name] = defaults[name] ? "true" : "false";
|
|
||||||
}
|
|
||||||
return this._super(defaults);
|
|
||||||
},
|
|
||||||
get_value: function () {
|
get_value: function () {
|
||||||
switch (this.$element.val()) {
|
switch (this.$element.val()) {
|
||||||
case 'false': return false;
|
case 'false': return false;
|
||||||
|
@ -1242,6 +1240,24 @@ openerp.web.search.ManyToOneField = openerp.web.search.CharField.extend({
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
facet_for: function (value) {
|
||||||
|
var self = this;
|
||||||
|
if (value instanceof Array) {
|
||||||
|
return $.when(new VS.model.SearchFacet({
|
||||||
|
category: this.attrs.string,
|
||||||
|
value: value[1],
|
||||||
|
app: this.view.vs
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return new openerp.web.Model(this.attrs.relation)
|
||||||
|
.call('name_get', [value], {}).pipe(function (names) {
|
||||||
|
return new VS.model.SearchFacet({
|
||||||
|
category: self.attrs.string,
|
||||||
|
value: names[0][1],
|
||||||
|
app: self.view.vs
|
||||||
|
});
|
||||||
|
})
|
||||||
|
},
|
||||||
start: function () {
|
start: function () {
|
||||||
this._super();
|
this._super();
|
||||||
this.setup_autocomplete();
|
this.setup_autocomplete();
|
||||||
|
@ -1250,51 +1266,6 @@ openerp.web.search.ManyToOneField = openerp.web.search.CharField.extend({
|
||||||
function () { started.resolve(); });
|
function () { started.resolve(); });
|
||||||
return started.promise();
|
return started.promise();
|
||||||
},
|
},
|
||||||
setup_autocomplete: function () {
|
|
||||||
var self = this;
|
|
||||||
this.$element.autocomplete({
|
|
||||||
source: function (req, resp) {
|
|
||||||
if (self.abort_last) {
|
|
||||||
self.abort_last();
|
|
||||||
delete self.abort_last;
|
|
||||||
}
|
|
||||||
self.dataset.name_search(
|
|
||||||
req.term, self.attrs.domain, 'ilike', 8, function (data) {
|
|
||||||
resp(_.map(data, function (result) {
|
|
||||||
return {id: result[0], label: result[1]}
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
self.abort_last = self.dataset.abort_last;
|
|
||||||
},
|
|
||||||
select: function (event, ui) {
|
|
||||||
self.id = ui.item.id;
|
|
||||||
self.name = ui.item.label;
|
|
||||||
},
|
|
||||||
delay: 0
|
|
||||||
})
|
|
||||||
},
|
|
||||||
on_name_get: function (name_get) {
|
|
||||||
if (!name_get.length) {
|
|
||||||
delete this.id;
|
|
||||||
this.got_name.reject();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.name = name_get[0][1];
|
|
||||||
this.got_name.resolve();
|
|
||||||
},
|
|
||||||
render: function (defaults) {
|
|
||||||
if (defaults[this.attrs.name]) {
|
|
||||||
this.id = defaults[this.attrs.name];
|
|
||||||
if (this.id instanceof Array)
|
|
||||||
this.id = this.id[0];
|
|
||||||
// TODO: maybe this should not be completely removed
|
|
||||||
delete defaults[this.attrs.name];
|
|
||||||
this.dataset.name_get([this.id], $.proxy(this, 'on_name_get'));
|
|
||||||
} else {
|
|
||||||
this.got_name.reject();
|
|
||||||
}
|
|
||||||
return this._super(defaults);
|
|
||||||
},
|
|
||||||
make_domain: function (name, operator, value) {
|
make_domain: function (name, operator, value) {
|
||||||
if (this.id && this.name) {
|
if (this.id && this.name) {
|
||||||
if (value === this.name) {
|
if (value === this.name) {
|
||||||
|
|
|
@ -11,6 +11,8 @@ Contents:
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
search-view
|
||||||
|
|
||||||
getting-started
|
getting-started
|
||||||
production
|
production
|
||||||
widgets
|
widgets
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
Search View
|
||||||
|
===========
|
||||||
|
|
||||||
|
Loading Defaults
|
||||||
|
----------------
|
||||||
|
|
||||||
|
After loading the view data, the SearchView will call
|
||||||
|
:js:func:`openerp.web.search.Input.facet_for_defaults` with the ``defaults``
|
||||||
|
mapping of key:values (where each key corresponds to an input).
|
||||||
|
|
||||||
|
The default implementation is to check if there is a default value for the
|
||||||
|
current input's name (via :js:attr:`openerp.web.search.Input.attrs.name`) and
|
||||||
|
if there is to convert this value to a :js:class:`VS.models.SearchFacet` by
|
||||||
|
calling :js:func:`openerp.web.search.Input.facet_for`.
|
||||||
|
|
||||||
|
Both methods should return a ``jQuery.Deferred<Null|VS.model.SearchFacet>``.
|
||||||
|
|
||||||
|
There is no built-in (default) implementation of
|
||||||
|
:js:func:`openerp.web.search.Input.facet_for`.
|
||||||
|
|
||||||
|
Providing auto-completion
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
An important component of the unified search view is the faceted autocompletion
|
||||||
|
pane. In order to provide good user and developer experiences, this pane is
|
||||||
|
pluggable (value-wise): each and every control of the search view can check for
|
||||||
|
(and provide) categorized auto-completions for a given value being typed by
|
||||||
|
the user.
|
||||||
|
|
||||||
|
This is done by implementing :js:func:`openerp.web.search.Input.complete`: the
|
||||||
|
method is provided with a value to complete, and the input must either return
|
||||||
|
a ``jQuery.Deferred<Null>`` or fetch (by returning a ``jQuery.Deferred``) an
|
||||||
|
array of completion values.
|
||||||
|
|
||||||
|
.. todo:: describe the shape of "completion values"?
|
||||||
|
|
||||||
|
Converting to and from facet objects
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. todo:: merge in changelog instead
|
||||||
|
|
||||||
|
The displaying of the search view was significantly altered from OpenERP Web
|
||||||
|
6.1 to OpenERP Web 6.2: it went form a form-like appearance (inherited from
|
||||||
|
previous web client versions and ultimately from the GTK client) to a
|
||||||
|
"universal" search input with facets.
|
||||||
|
|
||||||
|
As a result, while the external API used to interact with the search view does
|
||||||
|
not change the internal details — including the interaction between the search
|
||||||
|
view and its widgets — is significantly altered:
|
||||||
|
|
||||||
|
Widgets API
|
||||||
|
+++++++++++
|
||||||
|
|
||||||
|
* :js:func:`openerp.web.search.Widget.render` has been removed
|
||||||
|
* Search field objects are not openerp widgets anymore, their ``start`` is
|
||||||
|
not generally called
|
||||||
|
|
||||||
|
Filters
|
||||||
|
+++++++
|
||||||
|
|
||||||
|
* :js:func:`openerp.web.search.Filter.is_enabled` has been removed
|
||||||
|
|
||||||
|
Many To One
|
||||||
|
+++++++++++
|
||||||
|
|
||||||
|
* Because the autocompletion service is now provided by the search view
|
||||||
|
itself, :js:func:`openerp.web.search.ManyToOneField.setup_autocomplete` has
|
||||||
|
been removed.
|
Loading…
Reference in New Issue