Technical Presentation of the new Web client and framework of OpenERP 6.1: architecture, technologies, API, best practices, Javascript pitfalls, etc.
DON'T FORGET to scroll each slide to read the NOTES.
See also the related presentation about the changes at the server API/framework level:
http://www.slideshare.net/openobject/openerp-61-framework-changes
5. There is no migration
Everything has been rewritten from scratch
Been at the community days, probably seen it. Not been and looked at OpenERP Web (6.1),
probably noticed.
I doubt there’s a line left from the 6.0 codebase (we even split the project itself out), meaning
there isn’t a single API left either, meaning you’ll have to rewrite everything as well.
6. Technological changes
6.0 6.1
Python (+ javascript) Javascript
Mako QWeb
CherryPy Werkzeug
Pages Single Page Interface
Page requests JSON-RPC
Client used to be mostly Python and HTML-ish templates, bulk of logic moved to javascript.
* Template language QWeb, reminiscent of Kid/Genshi (though not inspired by them), XML-
based
More applicative interaction, more stateful, no page requests and full reloads.
9. Deployment changes
• Embedded mode, primary M.O.
• Stand-alone mode for dev/testing
• Official OpenERP bundle embeds
* Web Client has become “just another” OpenERP addon, though remains a different project
* Standalone mostly to avoid full server reload when writing Python code
** Or to put proxy inbetween
11. Network
HTTP
XML-RPC JSON-RPC
OpenERP Web Client Browser
HTML
6.0’s network arch: typical web frontend, communicates with server via XML-RPC, with client
via regular HTTP requests (e.g. link clicks) and HTML pages
* Most client logic in the Web Client layer, not really a good fit as OpenERP has lots of
client state
* Tentative to migrate to more stateful browser-side (iframe then ~pjax), half-hearted
and unsuccesful
6.1 merges server and web’s python: web’s python becomes a JSON-RPC interface to
OpenERP (+ some specific services), vast majority of logic moves to browser
12. Network
HTTP
XML-RPC
JSON-RPC
OpenERP Web Client Browser
HTML
6.0’s network arch: typical web frontend, communicates with server via XML-RPC, with client
via regular HTTP requests (e.g. link clicks) and HTML pages
* Most client logic in the Web Client layer, not really a good fit as OpenERP has lots of
client state
* Tentative to migrate to more stateful browser-side (iframe then ~pjax), half-hearted
and unsuccesful
6.1 merges server and web’s python: web’s python becomes a JSON-RPC interface to
OpenERP (+ some specific services), vast majority of logic moves to browser
16. Layout
Action Manager
OpenERP’s actions (ir.actions) -> primary drivers of everything (pretty much)
No visuals, dispatches to various children based on action executed (window, client) or does
work itself and handles result (act_url, server, report_xml)
17. Layout
View Manager
That slide was hard work
Actual handling of window actions: parses view descriptions, initializes views and handles
inter-view communications, view switching, ...
23. Widgets
Base unit of (visual) work. All the blue boxes are “widgets” (a class of openerp web)
Widget ~ MVC view
~ Backbone.View
~ NSView
~ QAbstractItemView
~ Spine.Controller
Couldn’t use “View” due to confusion potential with OpenERP views (list, form, ...)
27. Widgets: delegation
• Create children widgets to manage sub-
sections
• Implementation detail of the widget
• Cooperative behavior between parent and
children
28. Widgets lifecycle
• Synchronous initialization (construction)
• Template rendering
• DOM insertion
• Asynchronous initialization (secondary)
• [life]
• Cooperative destruction
See code (and detail of APIs) later
* Difficulty of parent/child relationships is not leaving “dangling” events or children
** Recursively stops children widgets
** Removes widget from DOM
** Removes widget from parent
** Must explicitly stop children when “killing” them during normal parent life (e.g.
ActionManager)
30. $ var a;
$ a = 3.42;
$ a = "42";
$ a = true;
$ var array = [];
$ var obj = {};
$ "foo".indexOf('o');
1
$ (3).toString();
"3"
$ [1, 2, 3, 4, 5].slice(1, 3);
[2, 3]
Base language has Python similarities:
* Dynamic typing
* Literal booleans, strings, numbers, arrays, ~hashmap
* Object-oriented, methods on a lot of things
31. > if (condition) {
> // action
> }
> while (condition) {
> // body
> }
> for(init; end; each) {
> // stuff
> }
Statements-heavy, close to C in its core syntax: if, while, for, return (explicit), ...
32. $ function f1 () {
> // things
> }
$ b = f1;
$ var anon = function () {
> // body
> };
$ [1, 2, 3].forEach(function (item) {
> console.log(item);
> });
$ var a = 4;
$ var fn = function () {
> a = 5;
> };
$ a;
4
$ fn();
$ a;
5
* First-class functions (& higher-order functions)
* Anonymous functions
* Full closure (read/write)
34. Not Python
* Empty array, object is true (empty string is false)
* objects only have string keys, few methods (and no map-like methods)
* “for (i in array)” does not work “correctly”, “for (o in object)” is tricky, only iterates on key
* ‘key in object’?
* “weak” types (+/-; ==; ?)
* objects v primitives; typeof v instanceof
* Very small “standard library”: 10 global functions, 8 global constructors and the Math and
JSON namespaces... and the DOM (in browsers)
42. $ writeln(null);
null
$ typeof null;
"object"
$ null instanceof Object; $ NaN;
false NaN
$ typeof NaN;
$ writeln(undefined);
"number"
undefined
$ NaN === NaN;
$ typeof undefined;
false
"undefined"
$ NaN == NaN;
$ undefined === null;
false
false
$ isNaN(NaN);
$ undefined == null;
true
true
$ parseInt("foo");
$ writeln((function () {})());
NaN
undefined
$ parseInt("1foo");
$ writeln((function () { return; })());
1
undefined
$ 1 - "foo";
$ (function (a) { writeln(a); })();
NaN
undefined
$ +"foo";
$ writeln(({}).does_not_exist);
NaN
undefined
$ var no_such_variable;
$ writeln(no_such_variable);
undefined
Python -> None and exceptions (errors)
JS -> null (None), undefined, NaN and exceptions.
* null !== undefined, but null == undefined
* typeof null === “object”
* Access undefined property -> undefined
** Access property set to undefined -> also undefined
* NaN exists in Python but very rare, more common in JS due to weak typing e.g. +”foo”
** NaN != NaN, use isNaN
** typeof NaN === ‘number’
43. $ function Foo() {};
$ var foo = new Foo();
$ foo instanceof Foo;
true
$ var bar = Foo();
$ writeln(bar);
undefined
$ var a = function () { this.b = "b"; };
$ a.b = function () { this.c = "c"; };
$ a.b.c = function () {};
$ new a.b.c instanceof (a.b.c);
true
$ new a.b().c instanceof (a.b.c);
false
$ (new (function () { this.ok = true; })).ok;
true
$ var baz = new Foo;
JavaScript OO is “interesting” (mostly bad kind)
Not going to dwelve far into it, but knowledge of *constructors* useful as object layers are
generally sugar over JS OO.
* Constructors are functions used in special context
** Any function can be used as a constructor (!= is useful a constructor)
* new $type() vs $type()
* $type an expression, ends at first parens by default (kinda weird)
** new a.b.c -> new (a.b.c); new a.b().c -> (new a.b()).c
** parens optional
44. $ this;
[object DOMWindow]
Actual output: $ var fn = function (f) { f(this); }
[] $ var a = {b: fn};
$ this === window; $ fn(function (t) {
true > writeln(t === window);
> });
true
$ a.b(function (t) {
> writeln(t === a);
> });
true
$ new fn(function (t) {
> writeln(t instanceof fn);
> });
true
$ var o = {};
{}
$ fn.call(o, function (t) {
$ var c = {};
> writeln(t === o);
$ c.b = a.b;
> });
$ c.b(function (t) {
true
> writeln(t === c);
$ fn.apply(o, [function (t) {
> });
> writeln(t === o);
true
> }]);
true
* Call site decides of `this` in callee
* default (global, function): “global object” (browsers: window)
* Not transitive
** b() -> `this` is window
** a.b() -> `this` is a
** new a.b -> `this` is instance of `a.b`
** c.b = a.b; c.b() -> `this` is c
** Function#call, Function#apply -> `this` is arg0
45. $ var a = { $ a = {
> attr: 1, > attr: 1,
> b: function () { > b: function () {
> writeln(this.attr); > var self = this;
> return function () { > return function () {
> writeln(this.attr); > writeln(self.attr);
> }; > };
> } > }
> }; > };
$ var f = a.b(); $ var f = a.b();
1 $ f();
$ f(); 1
undefined
$ a = {
> attr: 1,
> b: function () {
> var fn = function () {
> writeln(this.attr);
> };
> return fn.bind(this);
> }
> };
$ var f = a.b();
$ f();
1
* In closures, use alias (`var self = this`)
** Function#bind, _.bind
** Widget#proxy
50. Callback Mess
1. Forwarding (all methods must take 1/2
callbacks)
2. Composition (waiting on multiple events)
3. Post-hoc decisions (act on an event which
may or may not have happened)
* Lower evolutivity
** Many methods need extra 2 params, harder to read &use
** Harder to add new params (callback @end)
** Params post-callbacks hard to read/unreadable
** Costlier for methods which may become async
*** sync -> async transformation horrible so want to have API correct early
* Ad-hoc mess of flags &stuff
* Need to complement with flags as well
54. • Webkit Developer Tools / CDT
• Firebug
• Dragonfly
• IE Developer Tools
Actual capabilities vary, some things might work better in some tools than in other
1. WDT/CDT ~ Firebug
2. Dragonfly
3. IE9 DevTools
4. IE8 DevTools
55. Console
* Can actually get CLI JS console (à la `python`): spidermonkey or nodejs
* Access to page content (and libraries)
* Test code snippets
** jsfiddle
** jsbin
** tinker.io
** ...
* Interactively inspect objects
56. Console API
``console`` object, functions for programmatic printing of information
* Basic logging (log, debug, info, warn, error, exception)
* Formatting (group/groupEnd)
* Timers (time/timeEnd)
* Profiling (profile/profileEnd)
* trace
* dir
* count
65. Classes & subclassing
* JS OO somewhat similar to Python’s
* Single-inheritance
* No sugar for classes, plumbing a bit hard to get your head around and verbose
** Helper: Class & Class.extend
** this._super, doesn’t play well with async
75. API: Form Fields
hr -> email field
{instance.web.form.widgets}
Also weird cycle/management
* $element set to parent (table cell), in start() (_super() call required)
76. • Field#value :: *
• Field#set_value :: * => Deferred
• Field#on_ui_change :: () => ()
• Field#update_dom :: () => ()
• Field#validate :: () => ()
#value -> current value for the widget
#set_value -> form sets value on widget
#on_ui_change -> signal value change via UI to form
* Should have set #value
# update_dom -> resync non-value DOM (readonly, invalid, required)
# validate -> set/unset “invalid”