Files
owl/README.md
T
Géry Debongnie 9d715cb40d update readme
2019-03-04 08:31:49 +01:00

10 KiB

Core Utility for Odoo Web Client

This is a POC, not at all production ready code!!! This application is a proof of concept of how the Odoo web client could be redesigned.

Installation

npm install

Main scripts

Testing

npm run test
npm run test:watch

Building app

npm run build
npm run minify

These commands will build the app in dist/app, and minify the js bundle

Developping

npm run dev

This will: build the app (without minification), then start a live-server with hot-reloading, and watch the file system to make sure that assets are properly rebuilt if necessary, and then reloaded.

Design goals:

  • declarative UI code: we want to be able to declare the UI state in a template and trust the framework to do the job as expected

  • possibility to have 'imperative' ui code: Odoo comes from an imperative stand point, and it will be easier to migrate to this new design if it is still possible to create and mount subwidgets manually. Also, for performance reasons, it makes it possible to 'drop down' an abstraction layer if necessary (thinking about large one2many lists for example)

  • minimalist: less code is easier to maintain. Also, we do not want to depend on external libraries too much (so, for example, core code does not use jquery or underscore)

  • simple to reason: if we need to choose between the ultimate design and a simpler design, but which requires a few more lines of code, we will go for the second option. For example, I think that 'observables values' could be better than our simple EventBus. But this would introduce more concepts that will be needed to be understood by our ecosystem. We want as few concepts/communications primitives as possible for our needs.

  • testable: each part of the code should be easy to test. For example, the action manager takes in its constructor parameters which implements the IRouter and IAjax interfaces. This makes it easy to test.

  • typesafe: the current implementation uses Typescript. I believe that using well defined interfaces will make our code

    • safer: no more undefined is not a function
    • simpler/faster: once we are aware of the exact structure of what we are dealing with, we can make simpler code.
  • simple, "not enterprisy". We would like that developping in Odoo feels as simple and natural as possible, without too many abstractions/files. I feel that this is not really important, but there is some value in seeing a code source tree with tens of files (instead of hundreds). For this reason, we made some 'imperfect' choices: for example, most interfaces are in the same file as the code that uses them the most. Or we try to avoid the 'manager' word if possible.

Not a goal: reactiveness, or bi-directional bindings. We want to stay at a slightly lower level of abstraction. Each widget can implement its own update pattern.

Architecture notes

This POC revolves around two main ideas:

  • an Environment
  • a generic Widget class

Environment: The environment is an object which gives access to many important global partsof the application. For example, the rendering engine QWeb, translation functions, router, services, ... The environment is supposed to be unique (otherwise, some strange things could happen if there are two different active routers for example), accessible to every widget (via its parent). Also, even though it will probably happen, it is not supposed to be imported directly by a piece of code. If some code needs access to the environment, it should be given to him. This makes our code easier to test, and more generic (could be reused in a different environment).

Widget: Widgets are UI building blocks. In this POC, we want to experiment with a different Widget API which has the following properties:

  • async hook: the willStart hook is asynchronous. We like that
  • composability: we want to be able to create subwidget in a simple declarative way
  • async rendering: because of the two previous properties, the rendering operation of a widget (not of QWeb) is asynchronous. This introduces a new class of challenges.
  • mounted/willUnmount hooks: they really are missing from current Odoo Widgets
  • uses a virtual dom: not really a goal, but it seems like this is necessary. Because of this, we had to reimplement QWeb to make it output a vnode representation of a widget.
  • has a reference to the application environment: it is given to the root widget and propagated to each sub widgets. This is the key to make this component generic and useful.

We have 3 main folders and 3 main files:

  • core/: this contains main building blocks for the rest of the application. For example, Widget. Everything in core/ should be generic and independant of any other files. This is why the Widget class is not aware of anything specific to Odoo: it does not know about the action manager (via its environment). There are two reasons for this restriction:

    1. this prevents code coupling. If no code in core depends on something external, this means that the abstraction is self sufficient
    2. this makes it easier to reason. One can read the code from core and understand it. Then the rest of Odoo builds on top of these abstractions.
    3. this forces us to have a better design. This is not easy to prove, but I think that generic code forces us to think more about its API.
  • services/: here, we put classes that will be available in the application environment. For example, the router, the action manager, the crash manager, the notification system, and so on. Code here can import core files, the Env type, or code from other services. However, no code from widgets/ should be necessary.

  • widgets/: this contains every widgets required by the Odoo web client. Code in this folder can import the Env type from the main env file, can import code from ../core/, can import other widgets, but should not import anything from the service/ folder. Also, it should not import any registry.

  • env.ts: the global odoo environment. In this file, we define two different things: the Env interface (which can be imported by pretty much everything else), and the makeEnvironment function (which should only be imported by the main bootstrap file). The Env interface is a description of what each odoo widget will be able to access. Also, it will be accessible by every widgets. This will basically remove the need for trigger_up.

  • registry.ts: this is the main registry where actions, fields, ... should be added. In a more normal application, this should not be necessary, but Odoo code is designed to be extended from the outside. Note that the registry is not a (direct) part of the environment. This is a conscious decision: if it was part of the application environment, external code that would want to add something to the registry would be forced to import the main environment. This would encourage code to mix 'declaration code', such as defining a widget and 'control code'. Also, this would be slightly more awkward for testing. And finally, a stronger argument is that the environment will most likely be started in an asynchronous way, so it does seems weird to wait for it before adding something to the registry.

  • main.ts: the main bootstrap file. This is the part that takes everything else, and make sure they are properly connected. Then, it make sure that the root widget is mounted to its desired location.

New QWeb directives

  1. t-on-*: allow binding event handlers on any kind of event
  2. t-widget: instantiate a widget at this location. It has sub directives: a. t-props: to pass properties to the widget constructor/updateProps methods b. t-key: useful to keep track of which widget is which, when created in a list c. t-keep-alive: makes sure the widget is not destroyed when removed from the DOM (note that it will still be destroyed when parent is destroyed)
  3. t-ref

Random Notes

  • Change of behaviour: The compilation of a template should have a unique root node (but sub templates can have multiple roots)

TO THINK ABOUT/TO DO

  • Qweb

    • check qweb tests and see if it is reasonable (escaping? safety/security?)

    • Expression evaluation: make sure we cannot inject JS in template function. For example, a t-props directive such as t-props="state.someValueFromServer" where the value would be something such as "};console.log('Owned': context.env.actionManager.doAction('fuck you'))

    • remove the "if (${exprID} || ${exprID} === 0) {"

    • need to implement t-extend (?)

    • improve qweb generated code: do not assign object/array if no props/attrs/children

    • improve qweb gen code: when building a vnode, propagate a structure with children/attrs/hooks, fill it properly by each directive, then and only then create node with minimal code

  • Widget

    • check if it is possible to remove parent reference in widget
    • default implementation of propsUpdated?
  • Rendering think about batching all patching updates in a nextanimationframe

  • when t-widget/t-on directives are compiled, the widget/eval context is actually available. Should we use that info to determine if bound methods exists on the widget?

  • allow t-if t-widget and t-else t-widget on same t tag

  • should we allow class and t-add-class to be additive?

Advantages/Pros/Cons

Cons:

  • big rewrite, lot of work

Pros:

  • much lighter (prototype code for web client with template engine, services, component system, and misc core code is less than the size of jquery. Also, current web client code is convoluted and too complex for what it does)

  • much simpler: declarative and composable widgets will be so much easier to learn. No need to be an expert in how a odoo widget works. Also, the architecture will be much closer to the state of the art in js frameworks (such as react+redux) So, new hires will be productive faster

  • core code independant of jquery/underscore/jquery ui/whatever.

  • faster speed of development: I expect composable widgets+declarative framework and better organized code to allow us to develop much faster in the future, because the fundamentals are much better

  • more powerful extension system: xpath on templates may modify widget hierarchy

  • more powerful responsive system: a t-if="env.isMobile" will allow the framework to create/destroy widgets/dom stuff on the fly.

So, Antony, do you choose AMBITION or MEDIOCRITY?