A recent commit fixes the props validation code to make it work
regardless of the rendering context (important with the recent
t-call-context directive). Unfortunately, it then breaks props
validation through slots, because it assumed that the parent node in the
virtual node was the parent of the component, but it is not necessarily
true.
To fix this, we can use a simple property of the template functions:
they are bound to the current instance of the component, so we can
simply use "this"
The code for props validation assumed that the rendering context was a
component. This was actually true when it was written, but is no longer
true since t-call-context was introduced.
Because of that, it would crash when trying to access the internals of
the component, such as the static components object.
The fix is simple: instead of passing the context to the props
validation code, which can now be anything, we pass the component node,
which is guaranteed to give a reference to the component (and also to
the app). This also make the code slightly simpler.
closes#1261
Previously, when wrapping errors in wrapError, if the error was not an
actual error object, we wouldn't set the cause property on the wrapping
error correctly. The "instanceof Error" check is simply there so that we
can know whether we can add the original errors message to the wrapping
error, but the line that sets the error's cause was mistakenly moved
into that condition.
This commit also fixes the wrapping error's message in the case of
non-Error objects, to avoid having "the following error occurred in
hookname:" with nothing after the colon which is confusing/misleading.
This commit makes all errors thrown in owl use a custom error class. The
main point of this is to always wrap user-code errors that happen during
the owl lifecycle so that they can be treated uniformly in onError by
checking the cause property, and also allows user code to differenciate
owl errors from non-owl errors reliably at runtime.
Before this commit, wrapping an error occurring in async code
would result in an unhandledpromise exception, because we created
another promise, that would be rejected and that we didn't catch.
Before this commit, the generated code for the component directive was
using the current context as the place to look for static informations
(such as the sub components). However, it is not entirely correct, since
the current context may be different than the current component (which
is easily accessed by using the this variable).
Also, while doing this, we fix some issues in the t-set directive, which
as calling lazy values with the wrong this.
If you declare a child component which is not actually a Component,
the error message is not very friendly and not very helpfull to find
what happens and which child component is wrong.
```
const ChildComponent = "not a component constructor";
class MyComponent extends Component {
static components = { ChildComponent };
}
```
This commit improves the type declaration for those working with Typescript
and adds a runtime check for javascript codebases
Previously, we were wrapping the entire renderFn in a try/catch, causing
errors during template execution to be caught and wrapped by
onWillRender/onRendered which is undesirable. Now we only wrap the hook
that's being registered.
With this commit, component only render child
components if they have different props (shallow
equality). Otherwise, we trust the reactivity
system to make sure that all impacted components
are updated
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.
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.
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.
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.
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".
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.
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.