[ADD] basics for smarter collections to back the listview and its components
bzr revid: xmo@openerp.com-20110817085532-4chvix1odshuebvc
This commit is contained in:
parent
ed9db7c2c7
commit
4ce473b8dc
|
@ -10,6 +10,9 @@ openerp.base.core = function(openerp) {
|
||||||
var initializing = false,
|
var initializing = false,
|
||||||
fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
|
fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
|
||||||
// The base Class implementation (does nothing)
|
// The base Class implementation (does nothing)
|
||||||
|
/**
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
openerp.base.Class = function(){};
|
openerp.base.Class = function(){};
|
||||||
|
|
||||||
// Create a new Class that inherits from this class
|
// Create a new Class that inherits from this class
|
||||||
|
|
|
@ -1179,7 +1179,164 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class
|
||||||
|
* @extends openerp.base.Class
|
||||||
|
*/
|
||||||
|
var Events = {
|
||||||
|
/**
|
||||||
|
* @param {String} event event to listen to on the current object, null for all events
|
||||||
|
* @param {Function} handler event handler to bind to the relevant event
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
|
bind: function (event, handler) {
|
||||||
|
var calls = this['_callbacks'] || (this._callbacks = {});
|
||||||
|
|
||||||
|
if (event in calls) {
|
||||||
|
calls[event].push(handler);
|
||||||
|
} else {
|
||||||
|
calls[event] = [handler];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} event
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
|
trigger: function (event) {
|
||||||
|
var calls;
|
||||||
|
if (!(calls = this._callbacks)) { return this; }
|
||||||
|
var callbacks = (calls[event] || []).concat(calls[null] || []);
|
||||||
|
for(var i=0, length=callbacks.length; i<length; ++i) {
|
||||||
|
callbacks[i].apply(this, arguments);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
var Record = openerp.base.Class.extend(/** @lends Record# */{
|
||||||
|
/**
|
||||||
|
* @constructs
|
||||||
|
* @extends openerp.base.Class
|
||||||
|
* @borrows Events#bind as this.bind
|
||||||
|
* @borrows Events#trigger as this.trigger
|
||||||
|
* @param {Object} [data]
|
||||||
|
*/
|
||||||
|
init: function (data) {
|
||||||
|
this.data = data || {};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} key
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
get: function (key) {
|
||||||
|
return this.data[key];
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param key
|
||||||
|
* @param value
|
||||||
|
* @returns {Record}
|
||||||
|
*/
|
||||||
|
set: function (key, value) {
|
||||||
|
this.data[key] = value;
|
||||||
|
this.trigger('change:' + key, this, value);
|
||||||
|
this.trigger('change', this);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Record.include(Events);
|
||||||
|
var Collection = openerp.base.Class.extend(/** @lends Collection# */{
|
||||||
|
/**
|
||||||
|
* @constructs
|
||||||
|
* @extends openerp.base.Class
|
||||||
|
* @borrows Events#bind as this.bind
|
||||||
|
* @borrows Events#trigger as this.trigger
|
||||||
|
* @param {Array} [records] records to initialize the collection with
|
||||||
|
* @param {Object} [options]
|
||||||
|
*/
|
||||||
|
init: function (records, options) {
|
||||||
|
options = options || {};
|
||||||
|
_.bindAll(this, '_onRecordEvent');
|
||||||
|
this.records = [];
|
||||||
|
this._byId = {};
|
||||||
|
this._proxies = {};
|
||||||
|
this._key = options.key;
|
||||||
|
this._parent = options.parent;
|
||||||
|
|
||||||
|
if (records) {
|
||||||
|
this.add(records);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {Object|Array} record
|
||||||
|
* @returns this
|
||||||
|
*/
|
||||||
|
add: function (record) {
|
||||||
|
var records = record instanceof Array ? record : [record];
|
||||||
|
|
||||||
|
for(var i=0, length=records.length; i<length; ++i) {
|
||||||
|
var instance = (records[i] instanceof Record) ? records[i] : new Record(records[i]);
|
||||||
|
instance.bind(null, this._onRecordEvent);
|
||||||
|
this._byId[instance.get('id')] = instance;
|
||||||
|
this.records.push(instance);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a record by its index in the collection, can also take a group if
|
||||||
|
* the collection is not degenerate
|
||||||
|
*
|
||||||
|
* @param {Number} index
|
||||||
|
* @param {String} [group]
|
||||||
|
* @returns {Record|undefined}
|
||||||
|
*/
|
||||||
|
at: function (index, group) {
|
||||||
|
if (group) {
|
||||||
|
var groups = group.split('.');
|
||||||
|
return this._proxies[groups[0]].at(index, groups.join('.'));
|
||||||
|
}
|
||||||
|
return this.records[index];
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get a record by its database id
|
||||||
|
*
|
||||||
|
* @param {Number} id
|
||||||
|
* @returns {Record|undefined}
|
||||||
|
*/
|
||||||
|
get: function (id) {
|
||||||
|
if (!_(this._proxies).isEmpty()) {
|
||||||
|
var record = null;
|
||||||
|
_(this._proxies).detect(function (proxy) {
|
||||||
|
return record = proxy.get(id);
|
||||||
|
});
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
return this._byId[id];
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Builds a proxy (insert/retrieve) to a subtree of the collection, by
|
||||||
|
* the subtree's group
|
||||||
|
*
|
||||||
|
* @param {String} section group path section
|
||||||
|
* @returns {Collection}
|
||||||
|
*/
|
||||||
|
proxy: function (section) {
|
||||||
|
return this._proxies[section] = new Collection(null, {
|
||||||
|
parent: this,
|
||||||
|
key: section
|
||||||
|
}).bind(null, this._onRecordEvent);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onRecordEvent: function (event, record, options) {
|
||||||
|
this.trigger.apply(this, arguments);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Collection.include(Events);
|
||||||
|
openerp.base.list = {
|
||||||
|
Events: Events,
|
||||||
|
Record: Record,
|
||||||
|
Collection: Collection
|
||||||
|
}
|
||||||
|
};
|
||||||
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
$(document).ready(function () {
|
||||||
|
var openerp,
|
||||||
|
create = function (o) {
|
||||||
|
if (typeof Object.create === 'function') {
|
||||||
|
return Object.create(o);
|
||||||
|
}
|
||||||
|
function Cls() {}
|
||||||
|
Cls.prototype = o;
|
||||||
|
return new Cls;
|
||||||
|
};
|
||||||
|
module('list-events', {
|
||||||
|
setup: function () {
|
||||||
|
openerp = window.openerp.init();
|
||||||
|
window.openerp.base.list(openerp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
test('Simple event triggering', function () {
|
||||||
|
var e = create(openerp.base.list.Events), passed = false;
|
||||||
|
e.bind('foo', function () { passed = true; });
|
||||||
|
e.trigger('foo');
|
||||||
|
ok(passed);
|
||||||
|
});
|
||||||
|
test('Bind all', function () {
|
||||||
|
var e = create(openerp.base.list.Events), event = null;
|
||||||
|
e.bind(null, function (ev) { event = ev; });
|
||||||
|
e.trigger('foo');
|
||||||
|
strictEqual(event, 'foo');
|
||||||
|
});
|
||||||
|
test('Propagate trigger params', function () {
|
||||||
|
var e = create(openerp.base.list.Events), p = false;
|
||||||
|
e.bind(null, function (_, param) { p = param });
|
||||||
|
e.trigger('foo', true);
|
||||||
|
strictEqual(p, true)
|
||||||
|
});
|
||||||
|
test('Bind multiple callbacks', function () {
|
||||||
|
var e = create(openerp.base.list.Events), count;
|
||||||
|
e.bind('foo', function () { count++; })
|
||||||
|
.bind('bar', function () { count++; })
|
||||||
|
.bind(null, function () { count++; })
|
||||||
|
.bind('foo', function () { count++; })
|
||||||
|
.bind(null, function () { count++; })
|
||||||
|
.bind(null, function () { count++; });
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
e.trigger('foo');
|
||||||
|
strictEqual(count, 5);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
e.trigger('bar');
|
||||||
|
strictEqual(count, 4);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
e.trigger('baz');
|
||||||
|
strictEqual(count, 3);
|
||||||
|
});
|
||||||
|
test('Mixin events', function () {
|
||||||
|
var cls = openerp.base.Class.extend({
|
||||||
|
method: function () { this.trigger('e'); }
|
||||||
|
});
|
||||||
|
cls.include(openerp.base.list.Events);
|
||||||
|
var instance = new cls, triggered = false;
|
||||||
|
|
||||||
|
instance.bind('e', function () { triggered = true; });
|
||||||
|
instance.method();
|
||||||
|
|
||||||
|
ok(triggered);
|
||||||
|
});
|
||||||
|
|
||||||
|
module('list-records', {
|
||||||
|
setup: function () {
|
||||||
|
openerp = window.openerp.init();
|
||||||
|
window.openerp.base.list(openerp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
test('Basic record initialization', function () {
|
||||||
|
var r = new openerp.base.list.Record({qux: 3});
|
||||||
|
r.set('foo', 1);
|
||||||
|
r.set('bar', 2);
|
||||||
|
strictEqual(r.get('foo'), 1);
|
||||||
|
strictEqual(r.get('bar'), 2);
|
||||||
|
strictEqual(r.get('qux'), 3);
|
||||||
|
});
|
||||||
|
test('Change all the things', function () {
|
||||||
|
var r = new openerp.base.list.Record(), changed = false, field;
|
||||||
|
r.bind('change', function () { changed = true; });
|
||||||
|
r.bind(null, function (e) { field = field || e.split(':')[1]});
|
||||||
|
r.set('foo', 1);
|
||||||
|
strictEqual(r.get('foo'), 1);
|
||||||
|
ok(changed);
|
||||||
|
strictEqual(field, 'foo');
|
||||||
|
});
|
||||||
|
test('Change single field', function () {
|
||||||
|
var r = new openerp.base.list.Record(), changed = 0;
|
||||||
|
r.bind('change:foo', function () { changed++; });
|
||||||
|
r.set('foo', 1);
|
||||||
|
r.set('bar', 1);
|
||||||
|
strictEqual(r.get('foo'), 1);
|
||||||
|
strictEqual(r.get('bar'), 1);
|
||||||
|
strictEqual(changed, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
module('list-collections-degenerate', {
|
||||||
|
setup: function () {
|
||||||
|
openerp = window.openerp.init();
|
||||||
|
window.openerp.base.list(openerp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
test('Fetch from collection', function () {
|
||||||
|
var c = new openerp.base.list.Collection();
|
||||||
|
c.add({id: 1, value: 2});
|
||||||
|
c.add({id: 2, value: 3});
|
||||||
|
c.add({id: 3, value: 5});
|
||||||
|
c.add({id: 4, value: 7});
|
||||||
|
var r = c.at(2), r2 = c.get(1);
|
||||||
|
|
||||||
|
ok(r instanceof openerp.base.list.Record);
|
||||||
|
strictEqual(r.get('id'), 3);
|
||||||
|
strictEqual(r.get('value'), 5);
|
||||||
|
|
||||||
|
ok(r2 instanceof openerp.base.list.Record);
|
||||||
|
strictEqual(r2.get('id'), 1);
|
||||||
|
strictEqual(r2.get('value'), 2);
|
||||||
|
});
|
||||||
|
test('Events propagation', function () {
|
||||||
|
var values = [];
|
||||||
|
var c = new openerp.base.list.Collection([
|
||||||
|
{id: 1, value: 5},
|
||||||
|
{id: 2, value: 10},
|
||||||
|
{id: 3, value: 20}
|
||||||
|
]);
|
||||||
|
c.bind('change:value', function (e, record, value) {
|
||||||
|
values.push(value);
|
||||||
|
});
|
||||||
|
c.get(1).set('value', 6);
|
||||||
|
c.get(2).set('value', 11);
|
||||||
|
c.get(3).set('value', 21);
|
||||||
|
deepEqual(values, [6, 11, 21]);
|
||||||
|
});
|
||||||
|
test('BTree', function () {
|
||||||
|
var root = new openerp.base.list.Collection(),
|
||||||
|
c = root.proxy('admin'),
|
||||||
|
total = 0;
|
||||||
|
c.add({id: 1, name: "Administrator", login: 'admin'});
|
||||||
|
c.add({id: 3, name: "Demo", login: 'demo'});
|
||||||
|
root.bind('change:wealth', function () {
|
||||||
|
total = (root.get(1).get('wealth') || 0) + (root.get(3).get('wealth') || 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
strictEqual(total, 0);
|
||||||
|
c.at(0).set('wealth', 42);
|
||||||
|
strictEqual(total, 42);
|
||||||
|
c.at(1).set('wealth', 5);
|
||||||
|
strictEqual(total, 47);
|
||||||
|
});
|
||||||
|
});
|
|
@ -40,4 +40,5 @@
|
||||||
<script type="text/javascript" src="/base/static/test/class.js"></script>
|
<script type="text/javascript" src="/base/static/test/class.js"></script>
|
||||||
<script type="text/javascript" src="/base/static/test/registry.js"></script>
|
<script type="text/javascript" src="/base/static/test/registry.js"></script>
|
||||||
<script type="text/javascript" src="/base/static/test/form.js"></script>
|
<script type="text/javascript" src="/base/static/test/form.js"></script>
|
||||||
|
<script type="text/javascript" src="/base/static/test/list-utils.js"></script>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in New Issue