Previously, a crash in a lifecycle hook for any reason would throw an
error whose stack trace started from the scheduler and contained only
the place where the hook was called by owl, but not the place where the
hook was registered by the user. This proved very difficult for users to
debug as they cannot really tell which component registered that hook.
This commit alleviates the issue by creating a new Error when the hook
is originally called, and wrapping the registered callback in a try
catch, throwing an error with the correct stack trace instead of the
error in the hook, and setting the error in the hook as the cause of
this synthetic error.
Previously, we would replace block declarations in some contexts with
the variable alone, and decalres the variable higher in the generated
code. Issues arise because whe sometimes try to replace "let b2" with
"b2" but end up matching "let b20" which is incorrect.
This commit fixes that by adding a space to the text that we are trying
to match ("let b2 " won't match "let b20")
Have a GrandParent which controls whether one of its GrandChildren is displayed or not.
First, the GrandChild is displayed. Then, change the state of the GrandParent in order to kill
the GrandChild.
Before this commit the GrandChild is only removed from the DOM, as bdom correctly works.
But it is not destroyed.
After this commit, the GrandChild is correctly destroyed.
Before this commit, portal was a Component, now is a directive.
This commit also clean some unused code, and fix an issue on the clean
optimization when a portal is found in a condition or a loop.
Every use case involving some sort of key set on a component would give birth to a leak in an async context:
- If a key of a component changed, the outdated one was never destroyed.
- destroyed component were never removed from their parent's reference map.
This commit solves both issues, that are tightly linked anyway.
Before this commit, Portal overrode the _render function for its
component node, which means it bypassed the error handling mechanism
that was implemented in that method. It could have been fixed by
duplicating the error handling code as well, but a better solution in my
opinion is to simply override the renderFn function. This is closer to
the actual intent of the portal implementation: wrap the result of the
rendering in a VPortal vnode.
Have a Component which has a Component node, and a dynamic t-call itself having
a Component node.
Before this commit, both children had the same `key`, (as in the key in parent.children, which registers on the parent all its children).
As a result, the scheduler was endlessly hanging.
After this commit, it works as expected.
This means that unrelated ids (eg the id of a template, variable or key)
not longer share the same incrementing counter, meaning that you no
longer see a variable named "v2" unless another variable "v1" was
generated previously, this is also true for block data.
We only use 2 const enums in the codebase, but they are defined in the
output, even though this is not useful in any way. This commit reduces
the final output by about 30 loc.
Have a component which does a render in its onWillPatch, onPatched, onMounted hooks.
Before this commit, the result was incorrect: the second rendering was not taken into account.
After this commit, those renderings are correctly applied at the price of a delayed render when the fiber
is in a critical state.
This commit fixes some issues with falsy attributes not being properly
set/removed in various situations. Also, the behaviour was not
consistent between normal attribute (key/value) and generic attributes
(pair or object)
Owl provides a way to manage errors occuring in component lifecycle
methods. However, before this commit, these errors were not always
logged or visible, which is very annoying in the common developer
workflow (doing something, checking it works, seeing no error but a
broken interface).
In this commit, we make sure errors are logged/throws in all cases:
- if an error occurs in a mounting operation => the promise is rejected
(which will log the error)
- if an error occurs after the mounting operation and is not handled by
any error handlers => the error will be logged (with console.error).
Also, in that case, this commit adds a warning to explain that owl
destroys the root component, which will help developers understanding
what happened.
The problem was that the compiler is based on the assumption that the
multi block received by the parser only occurs in some cases
where the structure of the template require a multi block, and it does
not work when we have random multiblock elsewhere.
We could fix the issue by modifying the code generator code to support
these usecases, or by simply removing these cases in the parser. Since
this seems more efficient, this is the approach taken by this commit.
Note that it was a good opportunity to simplify the parser.
Have a child component on which a render is triggered.
This component delays its willUpdateProps and makes a rendering during the willUpdateProps
Before this commit, renderings of the child were inconsistent across
its parent's renderings.
After this commit, it works as expected.
Before this commit, when defining a getter in the env passed to useSubEnv,
the value was read, losing the definition of the property.
After this commit, declaring a getter in the env works as expected:
the property stays a getter.
Before this commit, when defining a getter in the env passed to the App,
the value was read, losing the definition of the property.
After this commit, declaring a getter in the env works as expected:
the property stays a getter.
Before this commit, class inheritance when using the onError hook was unclear nay wrong.
After this commit, error handlers are called from the bottom up in the inheritance hierarchy.
If a handler doesn't rethrow the error, the handling stops there and no other handler is called.
If a handler does rethrow, the handlers declared in a parent class are executed.
In the following situation: A parent of B, B parent of C, with an error
when C is mounted, caught by B and retriggering a rendering in B, then
the onMounted hook of A wasn't properly called. This commit fixes this
problem.
Before this commit, there were situations where the reference numbers
were not properly set, which caused the blocks generated to crash
because the algorithm could not get correct references.
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.
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".
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.
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.
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.
t-out automatically escaped content when it is a string not marked
with the `markup` function
t-out renders the raw content if it is a Block, or if it has been marked
with the `markup` funtion.
t-esc has been kept since it is safe and is optimized to render text nodes.
all t-raw calls are in fact the same as t-out.
The deletion of a key in an observed object did clear the observers of
that key but did not clear the atoms created (if any) when the key existed
(e.g. on the key value if it was trackable). This can lead to a growing
set of atoms that are useless but kept in memory if a lot of keys are
added/deleted. The same thing can happen if a key value is changed many
times and the values are trackable.
Here we fix the problem by
- keeping tracks of the objects that have been observed by an external call
to the method "atom" (we call them seeds).
- remove all observer atoms that are not seeds for observers of at least
one key deletion or key value change.
Note the fix is in some sense partial: a user could use the "atom" primitive
making the new system uncapable of avoiding a leak (see test "atom on an
object with a getter 3" where a getter is used in a weird way in an
observed object).
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>
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
fine grained reactivity:
existing key in source changes --> only observer having read the key are notified
add/delete key in source changes --> all source observers are notified
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>
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.
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.
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.
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
This commit reintroduces some tests for the t-set directive and make
them pass. For that, it was necessary to adapt the qweb compiler in
order to get the following behaviors:
A t-set can affect parent contexts (up to the first parent tagged as
boundary) when the key changed is found in one of the parent contexts.
Some context are marked as boundaries in such a way that
- rendering contexts (e.g. components) cannot be modified via a t-set.
- a t-set in a t-call body or in a called template can never change a
context above the t-call context.
Code prettification has been done.
Snapshots have been modified.
The point is to have visibility on the development of the owl2 features.
This commit reintroduces some tests keeping them skipped in order to fulfill that purpose.
There still are some missing tests though.
We add some test for the t-on directive.
For making them pass, it was necessary to change the code produced by
compileTForeach: the const declaration is not done by using generateId
and there was some conflict with the variable names produced in
captureExpression. Consequently, many snapshots had to be changed.
Code prettification has been done too.
This commit brings back the possibility to translate text nodes and
the attributes "label", "title", "placeholder", and "alt" in an app
configured with a suitable translation function.
It is also possible to deactivate translations under a node via
the directive t-translation="off".
For flexibility it is possible to define the list of translatable
attributes in the app.
When fixing the absence of capture for arrow functions passed as props,
we unintentionally introduced a breaking change: bare function calls in
the arrow functions used to be called with the rendering context as
their this value and this was no longer the case.
This commit fixes that by intentionally not capturing the value of
functions that are called withing the arrow function.
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
25 changed files with 633 additions and 68 deletions
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.