Commit Graph

177 Commits

Author SHA1 Message Date
Géry Debongnie 05a57d6da5 [REF] component: small cleanup
This commit makes it simpler to understand the way fibers are assigned
to nodes.
2022-02-11 10:46:44 +01:00
Géry Debongnie e6e6c31632 [FIX] component: concurrency issue
When a parent and a child were rendered at the same time, it was
possible for the 2 renders to decrement the same fiber internal
counter, which meant that the render was stalled.
2022-02-11 10:46:44 +01:00
Mathieu Duckerts-Antoine a073568667 [IMP] slots: via prop 'slots'
The slot inner working has been reworked. A prop "slots" is now passed
explicitely to the component. It looks like

{ slotName_1: slotInfo_1, ..., slotName_m: slotInfo_m }

with the objects slotInfo_i with mandatory keys "__render", "__ctx",
and optional key "__scope" and possibly others.

Here is how a slotInfo object can be created:
A slotInfo object is normally created by setting in a template something
like

<div>
    <t t-set-slot="foo" t-set-scope="scope" param_1="var" param_2="3">
        content
        <t t-esc="scope.bool"/>
        <t t-esc="scope.num"/>
    </t>
</div>

and it will be used somewhere like

<div>
    <t t-esc="props.slots.foo.param_1"/>
    <t t-slot="foo" bool="other_var" num="5">
</div>

In the above example, the function "__render" produces the block dom
element for the content of the t-set-slot.
The context "__ctx" will have a key "scope" with value { bool: ..., num: 5 }
and "__scope" will be set to "scope".
2022-02-11 10:41:18 +01:00
Samuel Degueldre f3555cfae0 [IMP] misc: update typescript to 4.5.2 2022-02-11 10:41:18 +01:00
Géry Debongnie 629b379ea9 [IMP] components: rename onRender->onWillRender, add onRendered 2022-02-11 10:41:18 +01:00
Samuel Degueldre a400fc5e69 [IMP] reactivity: overhaul reactivity system
This commit makes the reactivity system more fine grained and makes it
more eager to stop observing keys or objects when they are modified,
this results in fewer "false positive" notifications.
2022-02-11 10:41:18 +01:00
Géry Debongnie 093218a067 [FIX] remove cyclic dependency, improve error typing (#982) 2022-02-11 10:41:18 +01:00
Lucas Perais (lpe) ed3e6dcbb6 [FIX] component, fiber: error_handling at the Fiber level
Before this commit, errors triggered at the level of the fiber (as opposed to at the level
of a component's rendering), were handled as the very top level of the rendering, that is,
in the scheduler.
This was wrong because components below in the rendering tree would not have a chance to handle their
children's or their own errors.

After this commit, error triggered in willPatch, onMounted and onPatched are correctly handled
at the closest component to where they were thrown.
2022-02-11 10:41:18 +01:00
Lucas Perais (lpe) ee5f6c7569 [FIX] component: error_handling on current component
Have a Child Compnent which has one component that succeeds and another
one that fails at its instanciation.
The Child component handles the Errors by rendering itself.

Before this commit, the error handling algorithm made impossible for the scheduler to finish.
This was because the current fiber was still counted as ongoing, when it was actually completed.

After this commit, this use case is handled correctly.
2022-02-11 10:41:18 +01:00
Bruno Boi 3fa1bb62f6 [IMP] env: env is now frozen, useSubEnv does not affect user env 2022-02-11 10:41:18 +01:00
Géry Debongnie af80cefa76 [FIX] component: fix lifecycle order 2022-02-11 10:41:18 +01:00
Géry Debongnie 1658d15b87 [REF] move event_bus into utils, readd 2 functions 2022-02-11 10:41:18 +01:00
Géry Debongnie 8c16790471 [REF] move app and compiler code around 2022-02-11 10:41:18 +01:00
Géry Debongnie 9b8c582b32 [REF] component: fix typescript error 2022-02-11 10:41:18 +01:00
Lucas Perais (lpe) e0c0306acd [IMP] component: re-introduce error handling in lifecycle 2022-02-11 10:41:18 +01:00
Samuel Degueldre bca6afeb90 [IMP] qweb: turn handlers into function expressions only
For the sake of consistency with vanilla JS, and to allow some things
that were previously not possible.
2022-02-11 10:41:18 +01:00
Mathieu Duckerts-Antoine 756d32daa0 [IMP] reactivity: Context replacement
Aim to replace the abstraction "Context" from OWL 1 with the new primitives
"atom" and "useState":

- notification is done only after a batch of modifications.
- observers are notified at most once for a batch.
- an observer of type component is notified (and rerendered)
  only if it does not have an ancestor that has to be notified for the
  same batch of operations (anywhere in the web of references!).
- notification of components is done on all levels "simultaneously".

Co-authored-by: Aaron Bohy <aab@odoo.com>
Co-authored-by: Géry Debongnie <ged@odoo.com>
Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com>
2022-02-11 10:41:18 +01:00
Bruno Boi 348b505e5f [IMP] tags: reintroduce inline css tag
The CSS tag is useful to define a css stylesheet in the javascript file:
```js
class MyComponent extends Component {
  static template = xml`
        <div class="my-component">some template</div>
    `;
  static style = css`
    .my-component {
      color: red;
    }
  `;
}
```

The `css` tag registers internally the css information. Then, whenever the first instance of the component is created, will add a <style> tag to the document <head>.

Original commit in Owl v1: 953778dc5
2022-02-11 10:41:18 +01:00
Mathieu Duckerts-Antoine 80cb6b7a91 [REF] Code prettification 2022-02-11 10:41:18 +01:00
Lucas Perais (lpe) 4cceb239dd [IMP] qweb, blockdom, components: t-on with modifiers
supported modifiers: capture, prevent, stop, self.
2022-02-11 10:20:09 +01:00
Lucas Perais (lpe) 2ae0149adb [IMP] blockdom: t-on supports synthetic and native event handler
Synthetic handler is a sort of event delegation that allows placing
only one listener on the document to improve performance. It is an opt-in option.

Native listener places the listener on the node itself.
2022-02-11 10:20:09 +01:00
Lucas Perais (lpe) 3eb63452e7 [FIX] qweb, component: remove support for t-on on component node 2022-02-11 10:20:09 +01:00
Mathieu Duckerts-Antoine ebd2e4324f [IMP] app: mount app in "first-child" position
We reintroduce the possibility to mount the app in first position in
a target. The option "self" has been dropped since it is now possible
for a component to have several top level nodes.
2022-02-11 10:20:09 +01:00
Mathieu Duckerts-Antoine a0e1af83ac [IMP] component: defaultProps application
We re-add the application of defaultProps. Note that the application
is done twice in dev mode.
2022-02-11 10:20:09 +01:00
Mathieu Duckerts-Antoine c16d8ed6de [IMP] component,qweb: props validation
We re-add the possibility to validate props when dev mode is active.
No change in the API right Now. The dev mode is activated via the
configure method of App class.
2022-02-11 10:20:09 +01:00
Samuel Degueldre 15e4c856da [FIX] component: correctly create a new node when previous is destroyed
Previously, when a component node had been created and destroyed, and
the corresponding component was then recreated, the destroyed node was
reused. This commit fixes that
2022-02-11 10:20:09 +01:00
Samuel Degueldre ced5d0f69f [IMP] *: re-add a bunch of tests 2022-02-11 10:20:09 +01:00
Géry Debongnie 8c71d99e5f [MOV] move lifecycle_hooks into component/ 2022-02-11 10:20:09 +01:00
Géry Debongnie e746574a1d [REF] initial prototype of owl 2 2022-02-11 10:18:01 +01:00
Samuel Degueldre 718c765e3b [FIX] qweb: correctly capture the scope of arrow functions in props 2021-12-03 14:08:05 +01:00
Samuel Degueldre 150d620b8e [REF] run prettier 2021-12-03 14:08:05 +01:00
Achraf (abz) 6950f8e628 [FIX] components/fiber: Call patch only if target is valid
Currently in some cases, adding an attachment via lognote creates a traceback.
Error : shouldPatch is true while `vnode` is not defined, so `patch()` failed
This is a hotfix correcting this problem by calling `patch()` only if `shouldPatch` is true **and** the `vnode` is set.

opw-2645203
2021-11-03 11:37:04 +01:00
Géry Debongnie 64db7777dd [FIX] component: async issue
This is a tricky commit. The key point is that the Fiber.complete
method, which commits a rendering to the DOM works like this: it
traverses the component tree, patch the corresponding DOM for each
component, calls the mounted/destroy hooks, and reset the currentfiber
of components to null, all synchronously.

However, this means that it is possible for components to initiate a
rendering (which create a new currentFiber) before the currentFiber is
reset to null, so the internal state of owl is corrupted. This can
occurs in a crash, as in the test that accompanies this commit.

To fix this, we take care of resetting the currentFiber first, while we
walk the component tree. Then, the internal state is always consistent
(i.e. a currentFiber to null means that there is no pending rendering)

closes #904
2021-10-04 14:57:30 +02:00
Géry Debongnie 17ae1d06c4 [IMP] component: better detection for dynamic component change
Before this commit, Owl could not detect that the underlying component
in a template such as <t t-component="{{state.child}}"/> was changing,
if the two components have the same tag as root element.

This is because the reconciliation is done at the vdom level, which does
not know about components.  To solve this, one could use a t-key to make
sure owl can make the difference.

With this commit, we can simply use our knowledge of the fact that we are
dealing with a dynamic component and autogenerate a suitable key.

closes #623
2021-07-05 10:09:23 +02:00
Géry Debongnie 27629cedfa [IMP] qweb: allow multiclasses in t-att-class object form
the low level method htmlelement.classList.add does not accept multiple
classes in one string, which is why, in owl, the expression

`<div t-att-class="{'a b c': value}" />`

did not work as one might expect. It is however very convenient in real
life templates, so this commit improve owl by adding support for this
feature.

closes #813
2021-07-05 10:09:23 +02:00
Géry Debongnie 3a93370ab6 [FIX] component: do not shadow the initial error in some cases
Before this commit, the error handling code simply destroyed the
application whenever an unhandled error occured in the owl rendering
process. This is perfectly fine, except that since the application is
potentially corrupted, the destroy code may crash as well. We simply
catch those errors to avoid shadowing the main issue.

closes #866
2021-07-05 10:09:23 +02:00
Géry Debongnie a6bb4d8ee1 [FIX] component: make sure default slot is applied
Before this commit, owl was erroneously defining default slots in most
cases, even though they are empty. The problem occurs when the content
of a component slots is a t-set-slot, and we remove that, then use the
rest as default slots, even though it is only reduced to a set of text
nodes.

With this commit, we only consider the content as default content if it
is not only a sequence of text nodes containing only spaces.

closes #882
2021-07-05 10:09:23 +02:00
Géry Debongnie caf842c482 [FIX] component: fix scoping issue in nested loops
The templates contained in a slot have to be rendered with the current
scope, but during the rendering of the sub component, which happens
later (after the willStart for the sub component). Therefore, we need to
save the scope that should be used for the slots, so we can access the
proper variables. This was done using an Object.assign() statement,
which is actually only a shallowclone for the own properties.

In this commit, we properly copy all the properties for the current
scope, even those contained in the prototype.

closes #855
2021-07-05 10:09:23 +02:00
Michael Mattiello (mcm) d08ea63565 [IMP] directive: make props override t-props
Before this commit, props and t-props were computed like
`Object.assign(props, t_props)`.
Now, it computes like `Object.assign(t_props, props)` so props will
override t-props.

closes https://github.com/odoo/owl/issues/886
2021-07-02 11:50:23 +02:00
Géry Debongnie 922eb7cd98 [FIX] component: propagate errors to parent
Before this commit, the error handling process was too naive: once an
error occurs in a rendering, owl catches it, looks for a component that
implements the catchError method, then calls it.

However, in real life, we sometimes need to rethrow that error (or
another one) to propagate the error to some parent handler. This error
needs to be handled by the closest parent component that implements
catchError.

This is what this commit implements: it wraps the catchError call in a
try/catch, then in case of errors, try to handle it by a parent.
2021-06-18 10:34:09 +02:00
Samuel Degueldre e646eb697e [FIX] qweb: fix order of lookup in t-component directive
Previously, the order of resolution was:
- components defined on the class of the current component
- components defined on the QWeb instance
- the current instance's context

This is unnatural because it doesn't go from most specific to least, as
the current instance's context is the most specific. This is also
fragile, as adding components to the QWeb instance can break unrelated
components.

This commit fixes that by making the lookup start with the current
component's context instead
2021-05-18 09:58:30 +02:00
Géry Debongnie 0290f63ba3 [FIX] component: various issues while mounting manually components
The initial problem solved by this commit is that it was possible to get
into a situation where a mounting/rendering was started, then the component was
updated, but then another mounting operation begins, and it tries to
reuse the previous rendering operation, which is no longer uptodate.

The underlying issue is that Owl did not track properly the various
internal state change of a component.  These issue should be solved by
the introduction of the status enum, which currently tracks 6 possible
states:

- CREATED
- WILLSTARTED
- RENDERED
- MOUNTED
- UNMOUNTED
- DESTROYED

This status number replaces the isMounted and isDestroyed boolean flags.
It has the advantage of making sure that the component is in a
consistent state (it is no longer possible to be destroyed and mounted,
for example)

Another advantage is that it gives us an easy way to track the fact that
a component has been rendered, but is not in the DOM.  This is a subtle
situation where some various events can happen, and we need to be able
to react to that case.

Note that there is a change of behaviour: if a component is mounted in a
specific target, then before the mounting is complete, the component is
mounted in another target, we no longer reject the first mounting
operation.
2021-02-08 10:59:28 +01:00
Géry Debongnie 19a47a7001 [IMP] component: add setup lifecycle hook 2021-02-03 15:32:38 +01:00
Géry Debongnie dfc782599b [FIX] crash when rendering component before mounting
This commit makes sure that Owl does not crash when a component is
created, then updated (for example, with a (observed) state change), and
then, some moment later, mounted.

The initial render is not useful, because it is not linked to a mounting
action anyway.  And it caused issues such as a crash when Owl tried to
patch it to a non existing target
2021-01-19 15:26:15 +01:00
Géry Debongnie 1a20cc57de [FIX] component can be updated while detached from the main DOM
This commit tries to improve the interactions involving unmounted
components, or components mounted in an htmelement which is detached
from the main DOM, and rendering actions.

The main example is mounting a component in detached div, to prepare all
children.  If we just mount the component, it will work as expected: the
full component tree is rendered in memory, and ready to be really
mounted at the desired target.

However, if before doing that, we update the component and call render
on it (for example, with a change in an observed state), then this
rendering will be ignored, and therefore, the full subcomponent tree is
not uptodate.

This commit will also solve another issue in the compatibility layer in
odoo: in the form renderer, we mount components with the adapter in a
div, which is not yet attached to the DOM. We then manually call the
mounted hook when on_attach_callback is called.  This means that before
this commit, any change to the components between the initial rendering
and the call to mounted will be ignored.

As a bonus, this commit has the effect of bringing closer the semantics
of render and mount operations, which is certainly good.

closes #823
2021-01-14 09:05:05 +01:00
Géry Debongnie d043d47754 [FIX] component: propagate correct info when reusing fibers
In some cases, a rendering initiated in some component is then remapped
into a larger rendering initiated by some parent.

If we have some components which implement shouldUpdate to return false,
then the following scenario can happen:

- some parent component is mounted (which triggers a rendering with
force: true => bypass the shouldUpdate)
- some sub component is updated and rerendered, AFTER the previous
rendering goes through it
- the sub component notices that there is an ongoing rendering, and
  remaps itself in the parent rendering

Before this commit, the new fiber in the subcomponent does not have
force flag set to true, so the new rendering for the subcomponent does
not go through its own children (if they have shouldUpdate=false)

Another more complex kind of scenaria can happen when a remapped
rendering happen with sub components with dynamic shouldUpdate. The
problem is the same at the end: the new rendering should ignore the
shouldUpdate, to make sure we have the last correct information.

With this commit, we make sure that the flag of the new fiber is set to
true.

closes #818
2021-01-07 10:08:29 +01:00
Géry Debongnie 2a53a9592e [FIX] component: force mounting subcomponents with shouldUpdate
Components can implement shouldUpdate to return false.  In that case,
renderings coming from above should be ignored.

However, if the component was unmounted and is remounted, we actually
need to force a rerendering in that case, so it is mounted, otherwise
the subcomponent is left in unmounted state, which means that rendering
are ignored.

closes #800
2020-12-11 15:19:37 +01:00
Géry Debongnie 8483cc805e [FIX/IMP] export browser object, prevent crash in private mode
This commit performs two tasks:

1. it exports the internal browser object (in owl.browser), for ease of
testing
2. it makes sure that Owl does not crash inside iframes in incognito
mode, because window.localStorage raises an exception in that case

closes #791
2020-11-20 10:55:08 +01:00
Géry Debongnie cb07c99d40 [IMP] owl: add a new mount method 2020-10-30 15:31:03 +01:00
Géry Debongnie 8d2b250fef [FIX] slots: prevent infinite loop in some cases
The reviewer for the commit on named slots inside named slots did not
notice that there was an infinite loop.  Because of his sloppiness, Owl
could block in an infinite loop when a named t-slots was defined inside
a subcomponent, but not as a direct child.
2020-10-23 12:14:46 +02:00