[ADD] basic loading of default values in faceted search view

bzr revid: xmo@openerp.com-20120320102846-g2ymabvt4qc4bwzg
This commit is contained in:
Xavier Morel 2012-03-20 11:28:46 +01:00
parent 16fddf9207
commit b39712cf3d
4 changed files with 154 additions and 120 deletions

View File

@ -1,23 +1,13 @@
*.pyc
.*.swp
.bzrignore
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
.*
*.egg-info
*.orig
build/
bin/yolk
bin/pil*.py
.project
.pydevproject
.settings
RE:^bin/
RE:^dist/
RE:^include/
RE:^share/
RE:^man/
RE:^lib/
RE:^doc/_build/

View File

@ -314,6 +314,7 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search
}
},
on_loaded: function(data) {
var self = this;
this.fields_view = data.fields_view;
if (data.fields_view.type !== '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));
}
var self = this,
lines = this.make_widgets(
this.make_widgets(
data.fields_view['arch'].children,
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
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 () {
delete this.view;
this._super();
},
render: function (defaults) {
// FIXME
return this._super(_.extend(this, {defaults: defaults}));
}
});
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
*
* @param {String} value value to complete
* @returns {jQuery.Deferred<null|Object>}
* @returns {jQuery.Deferred<null|Array>}
*/
complete: function (value) {
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 () {
throw new Error(
"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);
});
},
/**
* 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),
* enable the filter.
*
* @param {Object} defaults the search view's default values
*/
render: function (defaults) {
if (this.attrs.name && defaults[this.attrs.name]) {
this.classes.push('enabled');
this.view.do_toggle_filter(this, true);
}
return this._super(defaults);
facet_for: function (value) {
return $.when(new VS.model.SearchFacet({
category: this.attrs.string || this.attrs.name,
value: 'true',
app: this.view.vs
}));
},
get_context: function () {
if (!this.is_enabled()) {
@ -939,6 +950,13 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w
})), view);
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 () {
this._super();
this.filters.start();
@ -1096,6 +1114,17 @@ openerp.web.search.SelectionField = openerp.web.search.Field.extend(/** @lends o
return $.when.apply(null,
[{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 () {
var index = parseInt(this.$element.val(), 10);
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; }
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 () {
var self = this, d = $.Deferred(), selection = this.attrs.selection;
for(var index=0; index<selection.length; ++index) {
@ -1146,23 +1161,6 @@ openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @
['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 () {
switch (this.$element.val()) {
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 () {
this._super();
this.setup_autocomplete();
@ -1250,51 +1266,6 @@ openerp.web.search.ManyToOneField = openerp.web.search.CharField.extend({
function () { started.resolve(); });
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) {
if (this.id && this.name) {
if (value === this.name) {

View File

@ -11,6 +11,8 @@ Contents:
.. toctree::
:maxdepth: 2
search-view
getting-started
production
widgets

71
doc/search-view.rst Normal file
View File

@ -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.