diff --git a/README.md b/README.md index 43bdee3d..0f373746 100644 --- a/README.md +++ b/README.md @@ -1,233 +1,76 @@ +**Warning!** + +This is currently a proof of concept, definitely not a production-ready codebase. +We hope to use it in the Odoo web client soon. + +--- + # 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. +## Overview -## Installation +This repository contains some useful building block for building web applications. +We made some efforts to make sure the code is reusable and generic. However, since this is the basis for the Odoo web client, we will not hesitate to design +the code here to better match the Odoo architecture/design principles. + +Currently, this repository contains: + +- some utility functions/classes +- an implementation/extension of the QWeb template engine that outputs a virtual + dom (using the snabbdom library) +- a Component class, which uses the QWeb engine as its underlying rendering + mechanism. The component class is designed to be declarative, with + asynchronous rendering. + +In the future, this repository may includes other features. Here are some possible +ideas: + +- a (frontend) router could be included. +- a store base class (as in the flux/redux architecture) + +Note: the code is written in typescript. This does not mean that the main web +client will ever be converted to typescript (even though I would really like it). + +## Web client demo + +To test and play with the component class, there is a demo application located +in the _demo/_ folder. It is a rethink of what the web client could look like +if it is ever rebuilt from scratch. It is currently an experiment. But +obviously, we hope someday to be able to use this work and actually rewrite +completely the odoo web client. + +## Main scripts + +To install every dependency needed to play with this code: ``` npm install ``` -## Main scripts +To build a bundle of this as a library: -### Testing +``` +npm run build +``` + +To run tests: ``` npm run test npm run test:watch ``` -### Building app +To play with the demo application: ``` -npm run build -npm run minify +npm run demo:build # make a build in dist/app/ +npm run demo:dev # make a build in dist/app/, and make a live server to access it ``` -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 +The _demo:dev_ task 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: +## Documentation -- _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? +[Component](doc/component.md) +[QWeb](doc/qweb.md) diff --git a/doc/component.md b/doc/component.md new file mode 100644 index 00000000..763dcf60 --- /dev/null +++ b/doc/component.md @@ -0,0 +1,3 @@ +# Component + +hey diff --git a/doc/qweb.md b/doc/qweb.md new file mode 100644 index 00000000..b7c29ee8 --- /dev/null +++ b/doc/qweb.md @@ -0,0 +1,3 @@ +# QWeb + +hey