Compiling a template with a dash ("-") in its name generates an
invalid function name in the compiled code.
A template named `"my-component"` leads to the function
`function my-component(app, bdom, helpers) { ... }` which has an
invalid name.
closes#1333
Previously, if you were not the last person to publish to the
playground, your playground branch would be behind the remote and trying
to push to it would fail.
This commit attempts to pull the changes before updating owl so that
even if you were not the last person to push to the playground, as long
as the pull is a fast-forward, publishing to the playground won't fail.
This commit also adapts some of the status number manipulation to use
bitwise or instead of addition, since return status code can be both
positive or negative and may cancel one another. Using bitwise or
ensures than any non-zero code will make the status non-zero and stay
that way.
# v2.0.4
- [IMP] app: expose live apps for the devtools
- [FIX] compiler: support t-model radio group in t-foreach
- [IMP] runtime: improve useExternalListener typing
This commit exposes owl apps in a global variable so that the owl
devtools can hook themselves on these apps. While the devtools are not
yet ready to be merged, exposing the apps will allow us to test the
devtools in production scenarios while polishing the development of
them.
Have a radio group defined inside a t-foreach:
```xml
<t t-foreach="values" t-as="val" t-key="val">
<input name="radiogroup" t-att-value="val" t-model="state.radioGroup" />
</t>
```
Before this commit the algorithm that set the "checked" attribute on the current active
radio button according to the state did not support having a dynamic value (`t-att-value`)
After this commit, this use case works as we go look in the dynamic attributes too.
Previously the type of the target for useExternalListener was
HTMLElement or Window, this makes document an invalid target. Here there
is no reason to use the EventTarget interface instead, as
useExternalListener only uses methods from that interface and should
work with any event target.
Closesodoo/owl#1323
# v2.0.2
Some small bug fixes
- fix: compiler: correctly escape backslashes when emitting block string
- fix: reactivity: don't subscribe to keys when making reactive
- fix: t-call-context: fix capture making component available in ctx
- fix: t-call-context: make `this` unavailable in rendering context
t-call-context is a feature that's supposed to mask the rendering
context completely, but currently the component remains available
through `this`.
This commit stops treating `this` as a reserved word, so that it's
compiled to a lookup in the rendering context, and adds `this` to the
rendering context when binding the component's rendering function. With
these changes, `this` behaves the same as before when outside a
t-call-context, but when the rendering context is overriden, the
template can no longer access `this`. `this` still represents the
instance of the component inside of the rendering function since it's
needed by owl internally.
A side-effect of this change is that now the rendering context is no
longer the instance of the component by default, but is always an object
with the component in its prototype chain. This was already the case
before in some contexts (eg inside t-foreach, or inside components with
a t-set/t-call anywhere in its template). This can cause issues in rare
cases when a component method was called directly on the rendering
context, as before this change, the method's bound this would be the
component instance (except in a t-foreach, component with a
t-set/t-call, etc), while after this change it is now never the
component instance. When the method only reads on `this` there is no
issue as all the components properties are available on the rendering
contexts, but setting a value on `this` will write on the rendering
context and not the component which is likely a mistake.
While this is a breaking change, simply adding a t-set/t-call to any
template would break components that would be broken by this change,
with this in mind we decided to make this change anyway so that
developers get the error as early as possible in the development cycle
rather than having a seemingly inocuous change break code under them.
Previously, when using a component with a slot within a t-call with
t-call-context, the component would become available again inside the
slot despite the t-call-context. This was caused by the fact that the
capture helper function creates an object with the component as its
prototype which is incorrect. It should just use the previous context as
its prototype.
When attempting to create a reactive object, we first check if the
target can be made reactive, this is done with Object.toString, which
internally reads the Symbol.toStringTag on the underlying object. When
trying to make a reactive object from another, for example when
reobserving a reactive or when reading a reactive object from the
context of another, this would read the subscribe the original object to
the Symbol.toStringTag property.
This commit fixes that by calling Object.toString on the underlying
target object where applicable.
Previously, when a template contained backslashes, they were not escaped
when creating the blockstring, meaning they would be interpreted as an
escape sequence within the JS string. This means that backslashes
preceding most characters were completely ignored and didn't end up in
the final block, double backslashes were collapsed to a single one, and
backslashes that constituted valid escape sequences in JS would be
treated as those (eg, \n would be a newline).
When creating a JS string expression from a string value, all characters
with special meaning in JS should be escaped, we were correctly escaping
backticks as these would unexpectedly close the string if not escaped,
but forgot to escape backslashes. This commit fixes that.
closes#1300
# v2.0.2
- fix: compiler: do not look up ComponentNode in the context
- fix: t-model takes precedence over t-on-input
- fix: reactivity: fix issues with reactive objects in proto chain
Previously, when there was a reactive object in the prototype chain of a
different object, it would get notified of the first write to a property
that existed on the reactive object but did not yet exist on the other
object, despite the value of that property not actually getting written
to the reactive and hence the value not getting changed.
This was caused by the fact that we assumed that Reflect.set would set
the value on the target, when in fact, the value is set on the object
that underlies the `receiver`. When a reactive object is part of the
prototype chain, the first write on a key triggers the set trap but the
receiver is not the reactive object, and so Reflect.set doesn't modify
the target, but adds the new key to the object that's lower in the
prototype chain.
This commit fixes that by actually reading the value from the target
instead of assuming that it was changed by Reflect.set
This commit also removes the special symbols SKIP and TARGET, as there
is no reliable way to check whether these keys are present on the object
itself or on its prototype chain, which can cause issue when trying to
create reactive objects from objects with other reactive objects in
their prototype chain, or to create reactive objects from objects with
non-reactive objects in their prototype chain. To solve this problem, we
simply use a WeakMap that maps reactives to their targets, and a WeakSet
that contains all skipped objects.
With this commit, we make sure that the code for t-model is run before
t-on-input event handler. This is useful to make sure that the state
reflected by t-model is up-to-date.
closes#1295
With the introduction of t-call-context, there is now no guarantee that
the ComponentNode can be found in the rendering context, any attempt to
do so can crash when combined with t-call-context. This commit fixes
that by using `this` instead, which in compiled templates refers to the
component that is being rendered.
# v2.0.1
- fix: runtime: correctly throw an error for duplicate object keys
- fix: parser: give t-set-slot="default" priority over the content
- fix: blockdom: correctly reorder children in heterogeneous t-foreach
- fix: portal: correctly move portal content when target is after it
- fix: blockdom: fix event_catcher traceback when a parent component has an empty child
When a parent component has an empty child with a t-on
and an event is triggered inside the parent, blockdom
checks if there is an event_catcher to call.
Doing so, an empty child would cause an error.
This commit fixes that.
Previously, when trying to mount a portal into a target that would be
mounted in the same render as the portal itself, the portal content
could not be mounted correctly. In a previous attempt to fix it, a
mistake was made while writing the test causing us to incorrectly
believe in now worked, when in fact, it would just move the portal
content to the end of its parent but without changing it.
This commit fixes the issue by making the `moveBeforeDOMNode` method of
VNodes accept a second optional parameter which is the parent in which
the element should be moved, defaulting to the current parent. This
results in identical behaviour when a parent is not specified, but when
it is, the VNode is "reparented" to the passed parent, which is what is
now done by the Portal
Currently, the `moveBefore` method on VNodes assumes that the `other`
VNode it receives is of the same type, and that the entire VNode tree
below that other VNode has the exact same structure. While this is
correct in most cases, it breaks down when there is a VToggler somewhere
in the VNode tree, as the structure below a VToggler can be very
different from the structure below another VToggler that was created
from the same compiled code. For example, two iterations of a t-foreach
that contains a <t t-component="..."/> may spawn different components,
and different components obviously have different structures.
One way to fix this is to remove the assumption that the structure of
the `this` block tree in moveBefore is the same as the structure
of the `other` block tree, and instead, always give the concrete DOM
node before which we want to move the current VNode instead of giving it
a VNode and an afterNode as a fallback. One problem with this solution
is that it degrades performance in the "standard" case, where a
t-foreach contains no VToggler anywhere in its block tree, as retrieving
the first concrete DOM node requires calling firstNode() which
recursively traverses the entire tree.
To avoid this performance penalty in the standard case, we opt to only
go down this route whenever we encounter a VToggler when calling
`moveBefore`. This requires that we maintain two separate methods, one
to move a VNode before another VNode of assumed similar structure, which
is basically the current implementation of `moveBefore` for all VNode
types except VToggler, and one implementation that moves a VNode before
a concrete DOM node. This method needs to be implemented for all VNode
types, as all VNode types can be descendants of a VToggler. This method
will only be called from one place: the `moveBeforeVNode` method of the
toggler, which is the point where we realize that the assumption of
identical structure breaks down.
Co-authored-by: Bruno Boi <boi@odoo.com>
Currently, if a component has a default slot defined with t-set-slot,
and also content that compiles to something (eg, text or even a comment
node), the content takes priority over the t-set-slot. As t-set-slot is
more explicity, it should have priority.
Currently when checking for duplicate keys, we insert the value of the
key as is in a set then check for unicity against those. When the key is
an object, we check for duplicates based on object identity, whereas the
keys are used by owl as strings, and so using objects can cause
duplicate key errors that do not throw correctly but crash in the owl
internals.
This commit fixes that by making the duplicate checking code serialize
the key to string before insertion and when comparing against existing
keys.
# v2.0.0
Finally the official v2.0.0 release is ready. There are no feature nor fixes since
last beta release, because it is stable.
Thank you to everyone who contributed.
## Changelog
Owl 2.0 is a large improvement over 1.0. It brings a lot of new features, improvements,
and better APIs. The most important changes are:
- a completely overhauled slot API (in particular slot scopes, ...)
- a new reactivity system, similar to Vue. In particular, if props are equals, then
a sub component is not updated.
- new rendering engine, based on blockdom. This makes Owl much faster
- support for fragments: a template can have an arbitrary number of roots
A detailed changelog can be found [here](CHANGELOG.md).
The t-out directive is compiled internally into a LazyValue, which
represents a value that may or may not be created sometimes in the
future. It can also be reused more than once, and this is where there
may be an issue: if a component is contained in the lazyvalue, it needs
a unique key (coming from the t-foreach) to be properly indexed in the
parent children map. However, the LazyValue does not keep the key
information, so it is not able to provide it to its content.
The fix is then quite clear: the LazyValue class should store the key
information, and provides it to its content. This allows the LazyValue
to be used multiple times, in any place in a template.
closes#1270
Before this commit, the template compiler would guess the next block id
that will be generated when compiling the body of a tcall. This is
correct IF there are not nested t-call, but otherwise wrong, because the
next block id could be mixed up: the first t-call would save the next
block id (let's say n), then the inner t-call would also save the same
block id (so, n), will then generate its own block (n+1), then the outer
t-call would use the block n index instead of n+1
The best fix, in my opinion, is to make sure we get the next block var
name, so we do not have to guess. To do that, each compile block type
function needs to properly return the information.
closes#1267
# v2.0.0-beta-21
- fix: prevent side effects at template compilation
- fix: props validation: does not crash with t-call-context
- fix: make t-portal work in all cases
- fix: make props validation work through slots
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"
Before this commit, the portal wouldn't work when its target is created
after the portal content, since it wouldn't be able to mount the dom at
the correct location.
With this commit, we work around the issue by mounting the portal
content at the portal location, then when the Portal component is
mounted, moving it to its correct location.
The big downside with that approach is that the portal content is
(sometimes) rendered and mounted at a location, THEN mounted in another
location. I think that it is most of the time not an issue, but one
could argue that it is inconsistent: some specific code could work at
one point, then fail in a different very similar situation (for example,
iframes don't support very well being moved around). On the flip side,
having the portal work as expected is very useful, and may be worth the
tradeoff.
closes#1250
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
When creating the template node for a block, we create htmlelements and
set their (static) attributes. But this can have side effects. For
example, setting the src attribute for an img element will trigger a
request to fetch the image.
We avoid that issue by simply setting the html element template node
inside a <template/> element.
Note that I don't really see how to test this fix in jest: we don't have
a real browser, and no real way to check for this side effect.
closes#1257
The `useEffect` has two parameters:
* The `effect` function
* The `computeDependencies` function
The `effect` function always take as parameters the result
of the `computeDependencies` function.
Expliciting this allows to better understand the `useEffect`
behaviour and the following example in the doc:
```
useEffect(
(el) => el && el.focus(),
() => [ref.el]
);
```
This commit makes it so that when an error occurs in an owl app and none
of the registered error handlers are able to handle it, we rethrow the
error instead of just logging it to the console and swallowing it. This
allows users of owl to handle errors that happen in owl applications by
using event listeners for error and unhandledrejection events on the
window.
In app.js, `mount(Root, document.body, { dev: true, env });` crash because `body` is not available yet.
Therefore, moving the script into the body fix the issue.
Previously, event handlers would not work when an app was mounted in an
iframe, this is caused by a guard in the event handler that checks that
the target element is still in the document, but it doesn't check
against the correct document in the case of an iframe.
This commit changes the check to check against the target's
ownerDocument.
Before this commit, a crash could occur when a component with no props
is defined in a slot, and that slot is conditionally displayed in
multiple locations.
The reason for that is that the key provided to the callSlot function
was identical, so from the perspective of the component function, it was
not possible to make the difference between a component located in
either places. With this commit, we make sure that a unique key is used
when a slot is reused in a template (or if it is dynamic, because in
that case, we have no idea at compile time if it will be unique or not)
closes#1246
Previously, attempting to mount an app in an iframe would crash, saying
that the target is not a valid DOM element, this is because instanceof
checks do not work cross-frame as global objects do not have the same
identity in frames as with the main window. This commit fixes that by
making sure the target is an instance of HTMLElement of the
corresponding window, and checks that the corresponding document body
contains it.
Recently, we made it so that when a component is rendered, it always
updates the property values for computed properties. This was done by
wrapping the value in a String or Boolean object. One issue with this is
that wrapping a falsy value in a String doesn't yield an empty string,
but a string containing the value as text (eg new String(undefined) ->
"undefined"), which causes the value to not remain empty as per the
spec. This commit fixes that by adding a fallback to the empty string
for falsy values before converting to a String object.
closes: #1236
We have been using this type in o-spreadsheet since https://github.com/odoo/o-spreadsheet/pull/1187
but the new typing file (https://github.com/odoo/owl/pull/1207) does not include it.
It would be useful to allow us to bump or version of owl witouht having to resort
to long aboslute paths (i.e. import from `@odoo/owl`and not
`@odoo/owl/dist/types/runtime/component@ everywhere).