Upgrade Training  Web Client
Migrating 6.0 web addons to 6.1
Migrating 6.0 web addons to 6.1
Rewriting 6.0 web  addons to 6.1
There is no migration                 Everything has been rewritten from scratchBeen at the community days, probably seen ...
Technological changes                         6.0                                     6.1             Python (+ javascript...
cloc: 6.0 Python               16059Javascript            9782Templates             5905
cloc: 6.1Javascript            16925Templates             3693  CSS                 3064 Python               1874
Deployment changes          • Embedded mode, primary M.O.          • Stand-alone mode for dev/testing          • Official O...
Architecture
Network                                                    HTTP                    XML-RPC                 JSON-RPC    Ope...
Network                                                  HTTP                                          XML-RPC            ...
Layout
LayoutWeb Client
Layout                Header                 Menu   Menu(secondary)
Layout                                        Action ManagerOpenERP’s actions (ir.actions) -> primary drivers of everythin...
Layout                                         View ManagerThat slide was hard workActual handling of window actions: pars...
LayoutView (form)
Layout  View (not form)View (not form either)
Layout: summary•   WebClient    •   Action Manager        •   View Manager (?)            •     Views (n)        •   Clien...
Reusable pieces   Dashboard (form view)
Reusable pieces                   action manager                           & action manager     view manager & client acti...
WidgetsBase unit of (visual) work. All the blue boxes are “widgets” (a class of openerp web)Widget ~ MVC view~   Backbone....
Widget Responsibilities• Drawing on-screen (template rendering)• User events handling (DOM events)• Delegation
Widgets: templates• Built-in handling• Only trivial information• Widget DOM root set up during rendering
Widgets: events• No special provisions• Use regular DOM events handling
Widgets: delegation• Create children widgets to manage sub-  sections• Implementation detail of the widget• Cooperative be...
Widgets lifecycle           • Synchronous initialization (construction)           • Template rendering           • DOM ins...
JavaScriptYour new frienemy.
$   var   a;        $   a =   3.42;        $   a =   "42";        $   a =   true;        $ var array = [];        $ var ob...
> if (condition) {         >     // action         > }         > while (condition) {         >     // body         > }    ...
$ function f1 () {        >     // things        > }        $ b = f1;        $ var anon = function () {        >     // bo...
$ var r = /foo/;        $ r.test("a foo");        true        $ r.exec("a foo");        ["foo"]Additional stuff:* Regex li...
Not Python* Empty array, object is true (empty string is false)* objects only have string keys, few methods (and no map-li...
DOM* Nodes & Elements
$ var root = document.getElementById(root);        $ root        [object HTMLDivElement]        $ var p = document.createE...
$ p.onclick = function () {        >     writeln("clicked paragraph");        > };        $ click(p);;        clicked para...
$ var $root = $(#root).empty();$ $root;[[object HTMLDivElement]]$ var $p = $("<p>", {title: "This is a paragraph"});$ $p;[...
Pitfallshttp://bonsaiden.github.com/JavaScript-Garden/
$ {>    var glob = 3;> }$ glob;3                                                     $ var undef;$ (function () {         ...
$ var somevar = 3; $ somevar; 3 $ somevar = 4; $ somevar;                              $ var fns = []; 4                  ...
$ writeln(null);null$ typeof null;"object"$ null instanceof Object;                                   $ NaN;false         ...
$ function Foo() {};        $ var foo = new Foo();        $ foo instanceof Foo;        true        $ var bar = Foo();     ...
$ this;[object DOMWindow]Actual output:                                   $ var fn = function (f) { f(this); }[]          ...
$ var a = {                                    $   a = {>     attr: 1,                                 >       attr: 1,>  ...
JavaScript concurrency
• JS runtime is a reactor (pattern) • Event loop • Single threaded • Blocking (synchronous)
Actually not what you’ll have with network requests: page (or even browser) frozen, no dialogno nothing.
• Continuation Passing Style (CPS) • Call long-running operation • Pass “continuation” callback • Called whenever operatio...
Callback Mess           1. Forwarding (all methods must take 1/2              callbacks)           2. Composition (waiting...
Deferred
• AKA Futures, promises            • (Success, Error)            • Pipelining            • Composition           • Promise...
Tooling
• Webkit Developer Tools / CDT            • Firebug            • Dragonfly            • IE Developer ToolsActual capabiliti...
Console* Can actually get CLI JS console (à la `python`): spidermonkey or nodejs* Access to page content (and libraries)* ...
Console API``console`` object, functions for programmatic printing of information* Basic logging (log, debug, info, warn, ...
Visual Debugger* Breakpoints (visual or programmatic)* Conditional breakpoints* call stack* Locals** CONSOLE
DOM Inspector* “Inspect Element” entry point* Edit DOM (as HTML text, add/remove/change attributes)* Visualize margin/padd...
Network*   List requests, time, latency, payload size*   Request & response header*   Request payload*   Response payload ...
ProfilerNothing much to say, usual visual profiler, have to learn on your own
Console (again)command-line API http://getfirebug.com/wiki/index.php/Command_Line_API
OpenERP Web APIs
6.1 Caveat• API stability (and the lack thereof)
• Python controller   • form.Widget• RPC (connection,    • Client actions  dataset)                      • Translations• Q...
Classes & subclassing* JS OO somewhat similar to Python’s* Single-inheritance* No sugar for classes, plumbing a bit hard t...
QWeb
Usage• openerp.web.qweb• QWeb.render(template, context) => String• t-name• t-call
Logic• t-set t-value• t-if• t-foreach t-as
Output•   t-esc        •   t-att•   t-escf       •   t-att-*•   t-raw        •   t-attf-*•   t-rawf
API: WidgetClient actionsregistries (instance.web.client_actions)<iframe class="youtube-player" type="text/html" width="64...
• Web-oriented keys: css, js, qweb• ‘static’ folder• module pattern: private namespace,  initialization code
• Widget#init :: * => ()• Widget#render :: () => String• Widget#start :: () => Deferred• Widget#stop :: () => ()
API: Search Fieldsmeetings
• Field            • non-standard cycle            • get_context, get_domain{instance.web.search.fields}* Weird cycle** Ren...
API: Form Fieldshr -> email field{instance.web.form.widgets}Also weird cycle/management* $element set to parent (table cell...
• Field#value :: *          • Field#set_value :: * => Deferred          • Field#on_ui_change :: () => ()          • Field#...
Debugging
Debugging withOpenERP Web
Debugging OpenERP       Web
Third-party EmbeddingInstall “share”Share button “link or embed”Put in HTML file on diskpython -mSimpleHTTPServer
Upcoming SlideShare
Loading in...5
×

OpenERP 6.1 - Web Framework Tutorial

17,945

Published on

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

Published in: Technology

Transcript of "OpenERP 6.1 - Web Framework Tutorial"

  1. 1. Upgrade Training Web Client
  2. 2. Migrating 6.0 web addons to 6.1
  3. 3. Migrating 6.0 web addons to 6.1
  4. 4. Rewriting 6.0 web addons to 6.1
  5. 5. There is no migration Everything has been rewritten from scratchBeen 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), meaningthere isn’t a single API left either, meaning you’ll have to rewrite everything as well.
  6. 6. Technological changes 6.0 6.1 Python (+ javascript) Javascript Mako QWeb CherryPy Werkzeug Pages Single Page Interface Page requests JSON-RPCClient 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-basedMore applicative interaction, more stateful, no page requests and full reloads.
  7. 7. cloc: 6.0 Python 16059Javascript 9782Templates 5905
  8. 8. cloc: 6.1Javascript 16925Templates 3693 CSS 3064 Python 1874
  9. 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
  10. 10. Architecture
  11. 11. Network HTTP XML-RPC JSON-RPC OpenERP Web Client Browser HTML6.0’s network arch: typical web frontend, communicates with server via XML-RPC, with clientvia 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 ofclient state * Tentative to migrate to more stateful browser-side (iframe then ~pjax), half-heartedand unsuccesful6.1 merges server and web’s python: web’s python becomes a JSON-RPC interface toOpenERP (+ some specific services), vast majority of logic moves to browser
  12. 12. Network HTTP XML-RPC JSON-RPC OpenERP Web Client Browser HTML6.0’s network arch: typical web frontend, communicates with server via XML-RPC, with clientvia 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 ofclient state * Tentative to migrate to more stateful browser-side (iframe then ~pjax), half-heartedand unsuccesful6.1 merges server and web’s python: web’s python becomes a JSON-RPC interface toOpenERP (+ some specific services), vast majority of logic moves to browser
  13. 13. Layout
  14. 14. LayoutWeb Client
  15. 15. Layout Header Menu Menu(secondary)
  16. 16. Layout Action ManagerOpenERP’s actions (ir.actions) -> primary drivers of everything (pretty much)No visuals, dispatches to various children based on action executed (window, client) or doeswork itself and handles result (act_url, server, report_xml)
  17. 17. Layout View ManagerThat slide was hard workActual handling of window actions: parses view descriptions, initializes views and handlesinter-view communications, view switching, ...
  18. 18. LayoutView (form)
  19. 19. Layout View (not form)View (not form either)
  20. 20. Layout: summary• WebClient • Action Manager • View Manager (?) • Views (n) • Client Action (?) • ...
  21. 21. Reusable pieces Dashboard (form view)
  22. 22. Reusable pieces action manager & action manager view manager & client action & list view
  23. 23. WidgetsBase unit of (visual) work. All the blue boxes are “widgets” (a class of openerp web)Widget ~ MVC view~ Backbone.View~ NSView~ QAbstractItemView~ Spine.ControllerCouldn’t use “View” due to confusion potential with OpenERP views (list, form, ...)
  24. 24. Widget Responsibilities• Drawing on-screen (template rendering)• User events handling (DOM events)• Delegation
  25. 25. Widgets: templates• Built-in handling• Only trivial information• Widget DOM root set up during rendering
  26. 26. Widgets: events• No special provisions• Use regular DOM events handling
  27. 27. Widgets: delegation• Create children widgets to manage sub- sections• Implementation detail of the widget• Cooperative behavior between parent and children
  28. 28. Widgets lifecycle • Synchronous initialization (construction) • Template rendering • DOM insertion • Asynchronous initialization (secondary) • [life] • Cooperative destructionSee 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)
  29. 29. JavaScriptYour new frienemy.
  30. 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. 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. 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)
  33. 33. $ 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>
  34. 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 andJSON namespaces... and the DOM (in browsers)
  35. 35. DOM* Nodes & Elements
  36. 36. $ 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)
  37. 37. $ 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 paragraph clicked 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]))
  38. 38. $ 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("clickedparagraph"); });$ $p.click();clicked paragraph[[object HTMLParagraphElement]]$ $p.click(function () { writeln("clicked paragraph2"); });$ $p.click();clicked paragraphclicked paragraph 2[[object HTMLParagraphElement]]
  39. 39. Pitfallshttp://bonsaiden.github.com/JavaScript-Garden/
  40. 40. $ {> var glob = 3;> }$ glob;3 $ var undef;$ (function () { $ writeln(undef);> var loc = 42; undefined> })();$ loc;Error: Cant find variable: loc$ var shadow = 3;$ (function () {> var shadow = 4;> writeln(shadow);> })(); $ does_not_exist;4 Error: Cant find variable: does_not_exist$ shadow; $ (function () {3 > does_not_exist = 42;$ (function () { > })();> writeln(local); $ does_not_exist;> var local = 3; 42> writeln(local);> })();undefined3Declared but not defined -> undefined (oddly makes sense)Function scope (!C, !java)* Python has UnboundLocalError v NameError, JS has undefined v ErrorImplicit declaration -> global
  41. 41. $ var somevar = 3; $ somevar; 3 $ somevar = 4; $ somevar; $ var fns = []; 4 $ for (var i=0; i != 10; ++i) { $ (function () { > fns.push(function () { writeln(i); }); > somevar = 5; > } > })(); $ fns.forEach(function (f) { f(); }); $ somevar; 10 5 10 $ (function () { 10 > var somevar = 42; 10 > (function () { 10 > somevar = 36; 10 > writeln(somevar); 10 > })(); 10 > writeln(somevar); 10 > })(); 10 36 36 $ somevar; 5* Writable closures (not possible in Python 2)* Danger! closures in loops
  42. 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; falsefalse $ isNaN(NaN);$ undefined == null; truetrue $ parseInt("foo");$ writeln((function () {})()); NaNundefined $ parseInt("1foo");$ writeln((function () { return; })()); 1undefined $ 1 - "foo";$ (function (a) { writeln(a); })(); NaNundefined $ +"foo";$ writeln(({}).does_not_exist); NaNundefined$ var no_such_variable;$ writeln(no_such_variable);undefinedPython -> 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. 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 aregenerally 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. 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. 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(); 1undefined $ 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
  46. 46. JavaScript concurrency
  47. 47. • JS runtime is a reactor (pattern) • Event loop • Single threaded • Blocking (synchronous)
  48. 48. Actually not what you’ll have with network requests: page (or even browser) frozen, no dialogno nothing.
  49. 49. • Continuation Passing Style (CPS) • Call long-running operation • Pass “continuation” callback • Called whenever operation completes
  50. 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
  51. 51. Deferred
  52. 52. • 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
  53. 53. Tooling
  54. 54. • Webkit Developer Tools / CDT • Firebug • Dragonfly • IE Developer ToolsActual capabilities vary, some things might work better in some tools than in other1. WDT/CDT ~ Firebug2. Dragonfly3. IE9 DevTools4. IE8 DevTools
  55. 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. 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
  57. 57. Visual Debugger* Breakpoints (visual or programmatic)* Conditional breakpoints* call stack* Locals** CONSOLE
  58. 58. 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?)
  59. 59. Network* List requests, time, latency, payload size* Request & response header* Request payload* Response payload (formatted JSON)
  60. 60. ProfilerNothing much to say, usual visual profiler, have to learn on your own
  61. 61. Console (again)command-line API http://getfirebug.com/wiki/index.php/Command_Line_API
  62. 62. OpenERP Web APIs
  63. 63. 6.1 Caveat• API stability (and the lack thereof)
  64. 64. • Python controller • form.Widget• RPC (connection, • Client actions dataset) • Translations• QWeb • underscore.js• Widget • Dialog• View • CrashManager
  65. 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
  66. 66. QWeb
  67. 67. Usage• openerp.web.qweb• QWeb.render(template, context) => String• t-name• t-call
  68. 68. Logic• t-set t-value• t-if• t-foreach t-as
  69. 69. Output• t-esc • t-att• t-escf • t-att-*• t-raw • t-attf-*• t-rawf
  70. 70. API: WidgetClient 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
  71. 71. • Web-oriented keys: css, js, qweb• ‘static’ folder• module pattern: private namespace, initialization code
  72. 72. • Widget#init :: * => ()• Widget#render :: () => String• Widget#start :: () => Deferred• Widget#stop :: () => ()
  73. 73. API: Search Fieldsmeetings
  74. 74. • 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)
  75. 75. API: Form Fieldshr -> email field{instance.web.form.widgets}Also weird cycle/management* $element set to parent (table cell), in start() (_super() call required)
  76. 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”
  77. 77. Debugging
  78. 78. Debugging withOpenERP Web
  79. 79. Debugging OpenERP Web
  80. 80. Third-party EmbeddingInstall “share”Share button “link or embed”Put in HTML file on diskpython -mSimpleHTTPServer

×