OpenERP 6.1 - Web Framework Tutorial

80
Upgrade Training Web Client

description

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

Transcript of OpenERP 6.1 - Web Framework Tutorial

Page 1: OpenERP 6.1 - Web Framework Tutorial

Upgrade Training Web Client

Page 2: OpenERP 6.1 - Web Framework Tutorial

Migrating 6.0 web addons to 6.1

Page 3: OpenERP 6.1 - Web Framework Tutorial

Migrating 6.0 web addons to 6.1

Page 4: OpenERP 6.1 - Web Framework Tutorial

Rewriting 6.0 web addons to 6.1

Page 5: OpenERP 6.1 - Web Framework Tutorial

There is no migrationEverything 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.

Page 6: OpenERP 6.1 - Web Framework Tutorial

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.

Page 7: OpenERP 6.1 - Web Framework Tutorial

cloc: 6.0

Python

Javascript

Templates

16059

9782

5905

Page 8: OpenERP 6.1 - Web Framework Tutorial

cloc: 6.1

Javascript

Templates

CSS

Python

16925

3693

3064

1874

Page 9: OpenERP 6.1 - Web Framework Tutorial

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

Page 10: OpenERP 6.1 - Web Framework Tutorial

Architecture

Page 11: OpenERP 6.1 - Web Framework Tutorial

Network

OpenERP Web Client Browser

HTTP

HTML

XML-RPC JSON-RPC

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

Page 12: OpenERP 6.1 - Web Framework Tutorial

Network

OpenERP Web Client Browser

HTTP

HTML

XML-RPCJSON-RPC

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

Page 13: OpenERP 6.1 - Web Framework Tutorial

Layout

Page 14: OpenERP 6.1 - Web Framework Tutorial

Layout

Web Client

Page 15: OpenERP 6.1 - Web Framework Tutorial

LayoutHeaderMenu

Menu (secondary)

Page 16: OpenERP 6.1 - Web Framework Tutorial

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)

Page 17: OpenERP 6.1 - Web Framework Tutorial

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

Page 18: OpenERP 6.1 - Web Framework Tutorial

Layout

View (form)

Page 19: OpenERP 6.1 - Web Framework Tutorial

Layout

View (not form either)

View (not form)

Page 20: OpenERP 6.1 - Web Framework Tutorial

Layout: summary• WebClient

• Action Manager

• View Manager (?)

• Views (n)

• Client Action (?)

• ...

Page 21: OpenERP 6.1 - Web Framework Tutorial

Reusable pieces

Dashboard (form view)

Page 22: OpenERP 6.1 - Web Framework Tutorial

Reusable pieces

action manager& client action

action manager&

view manager& list view

Page 23: OpenERP 6.1 - Web Framework Tutorial

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, ...)

Page 24: OpenERP 6.1 - Web Framework Tutorial

Widget Responsibilities

• Drawing on-screen (template rendering)

• User events handling (DOM events)

• Delegation

Page 25: OpenERP 6.1 - Web Framework Tutorial

Widgets: templates

• Built-in handling

• Only trivial information

• Widget DOM root set up during rendering

Page 26: OpenERP 6.1 - Web Framework Tutorial

Widgets: events

• No special provisions

• Use regular DOM events handling

Page 27: OpenERP 6.1 - Web Framework Tutorial

Widgets: delegation

• Create children widgets to manage sub-sections

• Implementation detail of the widget

• Cooperative behavior between parent and children

Page 28: OpenERP 6.1 - Web Framework Tutorial

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)

Page 29: OpenERP 6.1 - Web Framework Tutorial

JavaScript

Your new frienemy.

Page 30: OpenERP 6.1 - Web Framework Tutorial

$ 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

Page 31: OpenERP 6.1 - Web Framework Tutorial

> 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), ...

Page 32: OpenERP 6.1 - Web Framework Tutorial

$ 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)

Page 33: OpenERP 6.1 - Web Framework Tutorial

$ var r = /foo/;$ r.test("a foo");true$ r.exec("a foo");["foo"]

Additional stuff:* Regex literals (~Perl)** RegExp#test(String) -> Boolean** RegExp#exec(String) -> MatchArray** String#match(RegExp) -> MatchArray** String#search(RegExp) -> IndexNumber** String#replace(RegExp|String, String|Function) -> String** String#split([String|RexExp], [Number]) -> Array<String>

Page 34: OpenERP 6.1 - Web Framework Tutorial

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)

Page 35: OpenERP 6.1 - Web Framework Tutorial

DOM

* Nodes & Elements

Page 36: OpenERP 6.1 - Web Framework Tutorial

$ var root = document.getElementById('root');$ root[object HTMLDivElement]$ var p = document.createElement('p');$ p.setAttribute('title', "This is a paragraph");$ root.appendChild(p);$ p.appendChild(document.createTextNode("Text Content"));$ p.textContent = "text content";

$ root.childNodes[0];[object Text]$ root.childNodes[1] === p;true$ root.childNodes[0].nodeType;3$ root.childNodes[1].nodeType;1$ p.textContent;"text content"$ p.childNodes[0].data;"text content"

* Very verbose creation and query API [insert node/JSDOM examples** HTML5 APIs/modest improvements (querySelector, querySelectorAll)

Page 37: OpenERP 6.1 - Web Framework Tutorial

$ p.onclick = function () {> writeln("clicked paragraph");> };$ click(p);;clicked paragraph

$ p.onclick = function () {> writeln("clicked paragraph 2");> };$ click(p);clicked paragraph 2

$ p.addEventListener('click', function () {> writeln("clicked paragraph");> });$ click(p);clicked paragraph$ p.addEventListener('click', function () {> writeln("clicked paragraph 2");> });$ click(p);clicked paragraphclicked paragraph 2

* Events** “DOM0” simple but broken** DOM level 2 meh (add) to horrible (trigger)** MSIE < 9 incompatible (global event object, element.fireEvent(name[, options]))

Page 38: OpenERP 6.1 - Web Framework Tutorial

$ var $root = $('#root').empty();$ $root;[[object HTMLDivElement]]$ var $p = $("<p>", {'title': "This is a paragraph"});$ $p;[[object HTMLParagraphElement]]$ $root.append($p);$ $p.text("text content");$ $root.children("p");$ $root.find("p");$ $p.text();"text content"

$ $p.click(function () { writeln("clicked paragraph"); });$ $p.click();clicked paragraph[[object HTMLParagraphElement]]$ $p.click(function () { writeln("clicked paragraph 2"); });$ $p.click();clicked paragraphclicked paragraph 2[[object HTMLParagraphElement]]

Page 39: OpenERP 6.1 - Web Framework Tutorial

Pitfallshttp://bonsaiden.github.com/JavaScript-Garden/

Page 40: OpenERP 6.1 - Web Framework Tutorial

$ {> var glob = 3;> }$ glob;3$ (function () {> var loc = 42;> })();$ loc;Error: Can't find variable: loc

$ var shadow = 3;$ (function () {> var shadow = 4;> writeln(shadow);> })();4$ shadow;3$ (function () {> writeln(local);> var local = 3;> writeln(local);> })();undefined3

$ does_not_exist;Error: Can't find variable: does_not_exist$ (function () {> does_not_exist = 42;> })();$ does_not_exist;42

$ var undef;$ writeln(undef);undefined

Declared but not defined -> undefined (oddly makes sense)Function scope (!C, !java)* Python has UnboundLocalError v NameError, JS has undefined v ErrorImplicit declaration -> global

Page 41: OpenERP 6.1 - Web Framework Tutorial

$ var somevar = 3;$ somevar;3$ somevar = 4;$ somevar;4$ (function () {> somevar = 5;> })();$ somevar;5$ (function () {> var somevar = 42;> (function () {> somevar = 36;> writeln(somevar);> })();> writeln(somevar);> })();3636$ somevar;5

$ var fns = [];$ for (var i=0; i != 10; ++i) {> fns.push(function () { writeln(i); });> }$ fns.forEach(function (f) { f(); });10101010101010101010

* Writable closures (not possible in Python 2)* Danger! closures in loops

Page 42: OpenERP 6.1 - Web Framework Tutorial

$ writeln(null);null$ typeof null;"object"$ null instanceof Object;false

$ writeln(undefined);undefined$ typeof undefined;"undefined"$ undefined === null;false$ undefined == null;true$ writeln((function () {})());undefined$ writeln((function () { return; })());undefined$ (function (a) { writeln(a); })();undefined$ writeln(({}).does_not_exist);undefined$ var no_such_variable;$ writeln(no_such_variable);undefined

$ NaN;NaN$ typeof NaN;"number"$ NaN === NaN;false$ NaN == NaN;false$ isNaN(NaN);true$ parseInt("foo");NaN$ parseInt("1foo");1$ 1 - "foo";NaN$ +"foo";NaN

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’

Page 43: OpenERP 6.1 - Web Framework Tutorial

$ 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

Page 44: OpenERP 6.1 - Web Framework Tutorial

$ this;[object DOMWindow]Actual output:[]$ this === window;true

$ var fn = function (f) { f(this); }$ var a = {b: fn};$ fn(function (t) {> writeln(t === window);> });true$ a.b(function (t) {> writeln(t === a);> });true$ new fn(function (t) {> writeln(t instanceof fn);> });true{}$ var c = {};$ c.b = a.b;$ c.b(function (t) {> writeln(t === c);> });true

$ var o = {};$ fn.call(o, function (t) {> writeln(t === o);> });true$ fn.apply(o, [function (t) {> writeln(t === o);> }]);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

Page 45: OpenERP 6.1 - Web Framework Tutorial

$ var a = {> attr: 1,> b: function () {> writeln(this.attr);> return function () {> writeln(this.attr);> };> }> };$ var f = a.b();1$ f();undefined

$ a = {> attr: 1,> b: function () {> var self = this;> return function () {> writeln(self.attr);> };> }> };$ var f = a.b();$ f();1

$ 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

Page 46: OpenERP 6.1 - Web Framework Tutorial

JavaScript concurrency

Page 47: OpenERP 6.1 - Web Framework Tutorial

• JS runtime is a reactor (pattern)

• Event loop

• Single threaded

• Blocking (synchronous)

Page 48: OpenERP 6.1 - Web Framework Tutorial

Actually not what you’ll have with network requests: page (or even browser) frozen, no dialog no nothing.

Page 49: OpenERP 6.1 - Web Framework Tutorial

• Continuation Passing Style (CPS)

• Call long-running operation

• Pass “continuation” callback

• Called whenever operation completes

Page 50: OpenERP 6.1 - Web Framework Tutorial

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)

Callback Mess

* 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

Page 51: OpenERP 6.1 - Web Framework Tutorial

Deferred

Page 52: OpenERP 6.1 - Web Framework Tutorial

• AKA Futures, promises

• (Success, Error)

• Pipelining

• Composition

• Promises/A

• jQuery.Deferred, #pipe, jQuery.when

* Stateful, tri-state (pending, resolved, rejected)* Callback queues** Deferred#then** Deferred#done/Deferred#fail/Deferred#always* Single state change* $.Deferred#pipe: pipelining (async chains)* $.when: composition** Original: wrap value in deferred

Page 53: OpenERP 6.1 - Web Framework Tutorial

Tooling

Page 54: OpenERP 6.1 - Web Framework Tutorial

• 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 ~ Firebug2. Dragonfly3. IE9 DevTools4. IE8 DevTools

Page 55: OpenERP 6.1 - Web Framework Tutorial

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

Page 56: OpenERP 6.1 - Web Framework Tutorial

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

Page 57: OpenERP 6.1 - Web Framework Tutorial

Visual Debugger

* Breakpoints (visual or programmatic)* Conditional breakpoints* call stack* Locals** CONSOLE

Page 58: OpenERP 6.1 - Web Framework Tutorial

DOM Inspector

* “Inspect Element” entry point* Edit DOM (as HTML text, add/remove/change attributes)* Visualize margin/padding/borders* CSS adjustments** Metrics section (overview)** enable/disable rules* DOM breakpoints (flaky?)

Page 59: OpenERP 6.1 - Web Framework Tutorial

Network

* List requests, time, latency, payload size* Request & response header* Request payload* Response payload (formatted JSON)

Page 60: OpenERP 6.1 - Web Framework Tutorial

Profiler

Nothing much to say, usual visual profiler, have to learn on your own

Page 61: OpenERP 6.1 - Web Framework Tutorial

Console (again)

command-line API http://getfirebug.com/wiki/index.php/Command_Line_API

Page 62: OpenERP 6.1 - Web Framework Tutorial

OpenERP Web APIs

Page 63: OpenERP 6.1 - Web Framework Tutorial

6.1 Caveat

• API stability (and the lack thereof)

Page 64: OpenERP 6.1 - Web Framework Tutorial

• Python controller

• RPC (connection, dataset)

• QWeb

• Widget

• View

• form.Widget

• Client actions

• Translations

• underscore.js

• Dialog

• CrashManager

Page 65: OpenERP 6.1 - Web Framework Tutorial

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

Page 66: OpenERP 6.1 - Web Framework Tutorial

QWeb

Page 67: OpenERP 6.1 - Web Framework Tutorial

• openerp.web.qweb

• QWeb.render(template, context) => String

• t-name

• t-call

Usage

Page 68: OpenERP 6.1 - Web Framework Tutorial

Logic

• t-set t-value

• t-if

• t-foreach t-as

Page 69: OpenERP 6.1 - Web Framework Tutorial

Output• t-esc

• t-escf

• t-raw

• t-rawf

• t-att

• t-att-*

• t-attf-*

Page 70: OpenERP 6.1 - Web Framework Tutorial

API: Widget

Client actionsregistries (instance.web.client_actions)<iframe class="youtube-player" type="text/html" width="640" height="385" t-attf-src="http://youtube.com/embed/#{widget.params.id}" frameborder="0"></iframe>* sn2l2_v6Ur8 * pOA9PGYeP3E* oHg5SJYRHA0 * wmkoJGm6y6k* ZZ5LpwO-An4 * eQemvyyJ--g* qWkUFxItWmU * hXlzci1rKNM

Page 71: OpenERP 6.1 - Web Framework Tutorial

• Web-oriented keys: css, js, qweb

• ‘static’ folder

• module pattern: private namespace, initialization code

Page 72: OpenERP 6.1 - Web Framework Tutorial

• Widget#init :: * => ()

• Widget#render :: () => String

• Widget#start :: () => Deferred

• Widget#stop :: () => ()

Page 73: OpenERP 6.1 - Web Framework Tutorial

API: Search Fields

meetings

Page 74: OpenERP 6.1 - Web Framework Tutorial

• Field

• non-standard cycle

• get_context, get_domain

{instance.web.search.fields}* Weird cycle** Render everything (recursive, defaults)** Bind (element_id, in template context)** Start* dygraphs.com** dygraphs.com/dygraph-combined.js** new Dygraph(element, CSV_string)

Page 75: OpenERP 6.1 - Web Framework Tutorial

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)

Page 76: OpenERP 6.1 - Web Framework Tutorial

• 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”

Page 77: OpenERP 6.1 - Web Framework Tutorial

Debugging

Page 78: OpenERP 6.1 - Web Framework Tutorial

Debugging with OpenERP Web

Page 79: OpenERP 6.1 - Web Framework Tutorial

Debugging OpenERP Web

Page 80: OpenERP 6.1 - Web Framework Tutorial

Third-party Embedding

Install “share”Share button “link or embed”Put in HTML file on diskpython -mSimpleHTTPServer