Several directives (t-key, t-log, t-translation, …) are represented by
wrapper ASTs that contain another AST or null.
Because these wrappers don't share the same type as their children,
various AST type checks were broken.
This commit addresses those issues by:
- Commuting Translation / TranslationContext and Multi ASTs so that
Multi children are spread as expected (see parseChildren).
- Parsing the t-key directive before t-esc / t-out (in line with https://github.com/odoo/owl/pull/1685).
- Ensuring wrappers around TSet ASTs are recognized as having no
representation, so compileMulti properly discards children equivalent
to TSet.
The directives t-esc/t-out both discard the ASTs that are not of DomNode
type. Since the directives t-translation and t-translation-context both
creates AST wrappers of type TTranslation and TTranslationContext
respectively, mix t-esc/t-out and t-translation/t-translation-context
does not work. For example parse
<span t-esc="'Hello'" t-translation="off"/>
gives the AST
{
type: ASTType.TEsc,
expr: "'Hello'",
defaultValue: "",
}
This makes the span not been rendered in the end.
We fix that problem.
ARIA attributes containing human-readable text should be translated.
This commit adds human-readable ARIA attributes to the list of the
attributes translated by OWL.
# v2.7.0
- [IMP] runtime/utils: export htmlEscape and add tests
- [FIX] utils: Correct validation of mount target in shadow DOM/iframe
- [IMP] runtime: add markup tag function
markup tag function requires markup awareness to determine whether a
given parameter should be escaped or not.
This implies that pre-escaped content should be properly marked'ed up to
avoid double escaping. Having to manually wrap all calls to escape
with markup is cumbersome and prone to issues (on top of having to be
validated by the security team for no reason).
This commit introduces a markup-aware escape function to resolve those
issues.
Previously, `validateTarget` only checked if the target element or its
host (if it was a ShadowRoot) was directly contained in the document body.
This failed in cases where the target element was nested inside a shadow
DOM, which itself was attached to the document.
This commit introduces a new helper `isAttachedToDocument` that
traverses through parent nodes and shadow roots to ensure that the
target is ultimately attached to the given document.
Additionally, it now throws a clear error if `document.defaultView` is
missing, indicating that the target document is detached or invalid.
This ensures proper validation of mount targets, including complex
scenarios with shadow roots and iframes.
Allows markup to be called as a tag function. The interpolated strings
are then safely escaped for injection in HTML code.
Example usage:
```js
const maliciousInput = "<script>alert('💥💥')</script>";
const value = markup`<b>${maliciousInput}</b>`;
// no problem, maliciousInput is properly escaped
```
This commit removes an AST change during the code generation of slots.
Before, the `compileTSlot` function deleted the `t-props` attribute
directly on `ast.attrs`. This creates wrong code when compiling a
second time as the `t-props` attribute does not exist anymore.
The owl dev info message may be useful, but does not bring that much
value. Also, this is even slightly annoying while debugging odoo, since
it is common to have to go to dev mode, and the message takes some
visual space, which is a distraction. In this commit, we simplify it to
just warn that owl is in dev mode.
A new directive t-tanslation-context and a new family of directives (of the form
t-translation-context-...) are introduced to allow to translate terms in contexts.
Set t-translation-context="fr" on a node makes every call of the translation
function to be done with "fr" as seconde parameter when translating an
attribute/content within that node or its children (if no closer
t-translation-context directive is found).
A directive t-translation-context-attr="fr" can be used on a node to target its
attribute "attr". For example, if a div has an attribute title="a title",
use t-translation-context-title="pt" will make "a title" to be translated
in the context "pt". Note that this takes precedence over any other directive
t-translation-context found on a parent (or the div itself).
The translation function is in charge of the interpretation of the context: OWL
does not associate any meaning with a translation context.
Previously, we made it possible to compile templates ahead of time as
this was required by the owl devtools. Unfortunately, no effort was made
at the time to make this ahead-of-time compiler outside of the owl repo.
This commit refactors the AoT template compiler by splitting it in two
parts: a typescript module that gets bundled in the dist folder that
gives a programmatic way to call the standalone compiler from another
javascript module by importing it, and a command line utility that is
exposed as an executable files available to npm run-script of dependent
modules (configured in the bin field of owl's package.json).
This allows a simple workflow for modules wanting to use owl:
`npm install @odoo/owl`
add a script to your package.json that calls compile_owl_templates on
your source files, eg:
```jsonc
{
// ...
"scripts": {
"build": "compile_owl_templates src -o dist/templates.js"
}
}
```
`npm run build`
Without this, you only get the following error:
```
UncaughtPromiseError > OwlError
Uncaught promise > Invalid prop suffix
OwlError: Invalid prop suffix
```
Which is not helping much to find the problematic code.
This commit makes the alphabetical sort of object keys account for
numerical order as well so that it will feel right when handling objects
with numerical keys.
This commit solves issues with the observe variables functionality: it
first restricts refreshing the observed variables to when owl is loaded
on the page and then fixes how simplified paths are handled to find
components so that both type of path will work fine no matter from where
the function is called. This would cause issues when observing variables
from the observed state section.
This commit allows the user to observe variables so that their content
will be refreshed every 200ms and will be displayed as long as they
are found in the tree.
This commit fixes some dead code related to the display of arrays inside
other objects. Arrays are objects indeed so the condition was impossible
to satisfy.
This commit prevents the serializer to evaluate object getters when
it is trying to display an object in a reduced form. This would cause
issues when a getter was returning an error instead of a value.
Some big objects are quite hard to read because keys are sorted by
order of definition inside the object. It is better to sort them in
alphabetical order as is done in the base devtools.
The error recovery process is necessary, but is particularly subtle. An
error may occur basically any time some user code is called, which may
leave the internal owl state in an invalid situation.
In this commit, we try to improve Owl behaviour when a crash occurs in a
onMounted hook. Before this commit, it was possible for other component
mounted hooks to be called even though the component was not mounted, or
for willUnmount hooks to be called even though the component was
mounted, but its onMounted hooks had not been called.
All these situations need to be handled when we catch an error. In this
commit, we try to clean more internal state in the error handling code,
to make Owl more consistent.
# v2.5.0
- [IMP] app: add global values at compile time
- [IMP] parser: add support for custom directives
- [FIX] runtime: properly handle error caused in mounted hook
This commit, add a new configuration on the App: globalValues.
It's a global object of elements available at compilations.
For instance:
```js
const app = new App(SomeComponent, {
globalValues: {
plop: (string: any) => {
steps.push(string);
},
},
});
```
The plop function will be available at the compilation, so it can be
used on the templates :
```xml
<div t-on-click="() => __globals__.plop('click')" class="my-div"/>
```
This commit adds the support for custom directives. To use the
custom directive, an Object of functions needs to be configured on
the owl APP:
```js
new App(..., {
customDirectives: {
test_directive: function (el, value) {
el.setAttribute("t-on-click", value);
return el;
}
}
});
```
The functions will be called when a custom directive with the name of the
function is found. The original element will be replaced with the one
returned by the function.
This :
```xml
<div t-custom-test_directive="click" />
```
will be replace by :
```xml
<div t-on-click="value"/>
```
issue : https://github.com/odoo/owl/issues/1650
If a component onMounted hook causes an error, and an error handler
tries to reinitiate a new correct render, the fiber may be recycled. But
the scheduler removes it from its own set of tasks, so it will be
ignored.
We simply check for this condition before removing it from the tasks
set.
# v2.4.1
- [FIX] app: make subroots more robust
- [IMP] devtools: show objects class name and add special extension cases
- [IMP] devtools: allow expansion of empty class
- [IMP] Bump support for Node 20+
This commit fixes two issues with subroots:
1. creating a subroot create a new component node synchronously. This
would causes issues if the creation was done in the setup of a
component, since in that case, owl would reset the current component to
null right after, which would cause all calls to hooks to fail. This is
fixed by restoring the previous component node right after creating a
root.
2. the destroy method for roots calls the scheduler processTasks.
However, the processTasks method was not safe to reentrant calls,
which would in some cases crashes owl. For example, if a destroy is done
while a new component is mounted, the mount method would be called
twice.
This is fixed by ignoring the processTasks if we are currently
processing tasks. It works because the "for ... of" loop will still
process all new tasks in the current iteration.
This commit ensures that class names are displayed for non generic
objects and extensions of generic classes will have custom display for
their content.
# v2.4.0
- [IMP] owl: add basic support for sub roots
- [IMP] make set of timeout-able hooks (and their timeouts) clearer by using a const map
- [IMP] devtools: add support for file urls on chrome
In this commit, we extend the owl App class to support multiple sub
roots. This is useful for situations where we want to mount sub
components in non-managed DOM. This is exactly what the Knowledge app is
doing, with mounting views in an html editor.
Currently, this requires some difficult and fragile hacks, and still,
the result is that it is very easy to mix components from the main App
and a SubApp. But Knowledge does not actually care about creating a sub
app. It only needs the possibility to mount sub components in dynamic
places.
closes#1640
Also unnest the handling of `result` via guard clauses, and generate
messages as close as possible to use site, keeping the error
construction itself where it currently is as the goal is specifically
to point back to the *definition* site for the hook function.
This commit ensures that the hook will properly be loaded on file urls
when using chrome (this was already working in firefox). It is still
necessary to check the "Allow access to file URLs" option in the
extensions manager for this to work properly.
Before this commit, when willStart/willUpdateProps took more than
3s, a console.warn was done. In odoo, when a warning is logged
during a test, the test fails and the build is considered as "in
error".
There's a component that loads several resources (sequentially) in
its onWillStart, which *sometimes* takes more than 3s, making
builds fail non deterministically. Since a recent change (which
adds another call in the problematic onWillStart), the warning gets
logged quite often.
A quick fix is necessary, so we change the warn into a log, which
won't make build fail.
We may consider alternatives in the future though:
- add a parameter to onWillStart, to disable the timeout, or to
specify the delay (which is 3s by default)
- do not warn in test mode
Previously, if you wanted to pass a prop and have it be translated, you
had to either to the translation manually in JS, or use a workaround
with t-set and a body so that Owl would translate it for you, and then
pass the t-set variable as a prop. This is quite inconvenient and is a
common use case.
This commit introduces the `.translate` suffix to solve this issue. When
a prop uses this suffix, it is treated as a string instead of a JS
expression, avoiding the need for quotes as well as their escaping and
allowing extraction tools such as babel to generate a clean string as
the term's translation id. This is also more ergonomic. This suffix is
available for both component props and slot props.
This change will still require some work in Odoo to correctly extract
the terms for props using this suffix.
This commit fixes inline expressions when we have these conditions:
* inline expression that contains an arrow function
* inside the arrow function call a function with multiple arguments (3+)
* the second argument (not the first and not the last) must be a variable
In the `compileExprToArray` we have a code to handle missing tokens in an object
e.g.: {a} (equivalent to {a:a})
When OWL match all 3 conditions listed above we execute the code to handle
the missing tokens and so it alter the tokens and adds a new token:
`{ type: "COLON", value: ":" }`
This result in a Javascript compilation error:
OwlError: Failed to compile template "XXX": missing ) after argument list
To fix the error and avoid execute the code to handle the missing tokens, now,
we track also the parentheses in our local stack.
This commit adds basic autocomplete in xml files. This includes autocompletion
for elements, components, props, attributes, and javascript expressions.
It also:
- Adds "Go To Definition" support for props and javascript expressions in xml
- Support for the following directives: t-att, t-model, t-tag, t-debug, t-log
- Fixes t-else syntax highlight to be non-dynamic as the attribute value
should be empty
Previously, if you used a backslash in a template on the playground, it
would be interpreted as an escape sequence, and if you wrote "${" it
would likely crash, as it would be treated as an interpolation sigil in
the context of the script element injected inside the playground's
iframe.
A previous fix already escaped backticks, this commit completes the
escaping by escaping the two other things that have special meaning
within template literals.
Previously, there were a few places where the compiler would create
strings from template content and emit them as template literals, but
didn't properly escape characters or character sequences with special
meanings, in particular: backslashes, backticks, and interpolation
sigils.
This commit fixes this in:
- block creation (interpolation sigils were not escaped)
- text node creation (no escaping was performed)
- comment not creation (no escaping was performed)
- default values for t-esc (no escaping was performed)
- body of a t-set (no escaping was performed)
Previously, the async hook warning that's emitted when async hooks take
too long to resolve would be emitted even if the component was
destroyed or cancelled. We used to check if the node's fiber was still
the current fiber as a proxy for cancellation, but this is insufficient
in the case where an app might be destroyed.
This commit also wraps the content of some of the warning tests into a
try/finally block so that a failure of these tests doesn't pollute
setTimeout and console.warn and cause other tests to timeout one after
another.
# v2.2.10
- [IMP] runtime: allow using any class as a type in props validation
- Update reactivity.md
- [IMP] owl-vision: Better snippets
- [FIX] docs: code in example cannot run
Previously, we had a fixed whitelist for types that were allowed during
props validation. The implementation however supports using arbitrary
classes, and in practice it's desirable to do so, and already done when
not using typescript (when using typescript, it will error if the class
is not whitelisted), eg in Odoo, we use "Element" for the arch in the
standard view props, but this causes all view controllers to fail type
checking because it's not whitelisted.
This commit simply replaces existing constructors by a generic
constructor type, and adds a test with a validation success and a test
with a validation failure.
While Sets have better lookup complexity than arrays, because of the
large constant factors, small arrays can perform better than small sets
when checking for inclusion.
In practice, replacing both of the raw types sets with arrays can
improve performance of reactive-heavy workloads by as much as 30%.
Considering the reactivity code is very hot when rendering data-heavy
components, and the low impact on readability of the fix, the
cost-benefit analysis is clearly in favour of making the fix.
A template set can now obtain a template by calling the function
getTemplate (if any) received in the initial config.
The returned value can be an Element instance, a template string, a
function (i.e. a compiled template), or null. In the last case, owl will
look into the template set rawTemplates object.
Before to this commit, the .trim modifier did not work correctly. The value
in the model is always trim at each input event but not the visual value
in the input. This difference between the visual and the model value cause
few strange bug.
After reflection, we think that we always want to be in .lazy when we use .trim.
Because we want to trim the final value during the onchange event and not
at each input event. If we do it at each input event, we can't write more then
one word easily.
So this commit change the behavior of the .trim modifier to always be in .lazy
For some reason the code of parseXML was duplicated, despite being
exactly the same except for some whitespace. Move it out into a common
utils file.
closes#1569
# v2.2.7
- [IMP] types: correctly support `Function` type for props-validation
- [IMP] app: allow to instantiate templates lazily
- [FIX] compiler: compile named slot in t-component in named slot
- [IMP] devtools: update the devtools documentation
- [IMP] owl-vision: syntax scripts, single quotes attributes and slot props highlight and switch below command
- [IMP] doc: improve documentation of useExternalListener
- [FIX] devtools: remove highlights when out of devtools
- [IMP] devtools: highlight component on select
- [ADD] owl-vision: vscode extension initial commit
Previously, having a Function as a type in the static props description
of a component would only work if the component was not the root
component, as the static props description on Component was "any",
whereas the static props description on ComponentConstructor was
"Schema". This meant that static props description on non-root
components was not type-checked, and on root components it was
type-checked only on the mount call.
This commit makes it so that static type description is of type "Schema"
on Component, now causing static props description to be type-checked,
and adds `typeof Function` to the `BaseType` union, which allows
declaring that a component expects a function as a prop.
Closes#1448
With this commit, the templates that are given while instantiating the
App class can be an object with templates not yet parsed (i.e. string,
not Document). This allow to instantiate the App class with templates
that are not yet parsed, and these templates will be parsed only when
needed.
Part of task-id 3601257
Previously, if a t-set-slot was inside a t-component itself inside a
t-set-slot the parser would crash, because the slot is removed from the
template before compiling its content, causing a further check's
assumption to be broken (the t-set-slot remains connected to the
component's xml node)
This commit adds the following:
- Syntax builder scripts to make syntaxes easier to read and edit
- Syntax highlight in single quote attributes
- Syntax highlight for slot props
- Basic syntax highlight for xpaths
- Added `Switch Below` command
This commit fixes the following:
- Using `Switch Besides` or `Switch Below` does not open a new panel if one was already open
- Fixed missing space in component's snippet indentation
This commit fixes an issue with the highlight element functionality
which would leave the highlight active when the search bar is not empty
and the user changes view. It will now disappear as long as the user
moves his cursor anywhere in the page. Also, highlights are now removed
when the user scrolls while the html selector is active.
Small imp to highlight a component when it gets selected in the tree to
provide better visual feedback to the user when he is using arrow keys
navigation.
Owl Vision is a vscode extension that improves owl developpement by adding
syntax highlights in templates and commands to easly navigate between
components and templates.
It also adds a Component snippet.
Commands:
* `Owl Vision: Find Template`:
- If the cursor is on a template name, finds the corresponding template.
- If the cursor is on a component, finds the template of the selected component.
* `Owl Vision: Find Component`: Finds the selected component definition.
* `Owl Vision: Switch`: Finds the corresponding template or component
depending on the current file.
* `Owl Vision: Switch Besides`: Finds the corresponding template or component
depending on the current file and opens it besides.
Settings:
* `owl-vision.js.include`: Javascript files to include in search.
* `owl-vision.js.exclude`: Javascript files to exclude in search.
* `owl-vision.xml.include`: XML files to include in search.
* `owl-vision.xml.exclude`: XML files to exclude in search.
This commit fixes 2 issues specific to the firefox version of the
extension:
First issue concerns the computation the position of the
border between the subwindows of the components tab which could go
terribly wrong due to the fact that the innerwidth of the window is
implicitly set to 10 when the owl devtools window is hidden.
Second issue comes from changes in the runtime.onMessage method of
browser which now requires to directly return the response instead of
using the sendResponse method.
Also perform a little cleanup on usage of browserInstance and in the
manifest.
`useEffect` type is not compatible with later versions of Typescript.
(I tried with Typescript 5.2, I didn't check which specific
version had the breaking change)
This prevents other projects using owl (such as o-spreadsheet) to
upgrade their own version of Typescript.
`<T extends [...T]>` raises the two following errors:
`Type parameter 'T' has a circular constraint.ts(2313)`
`A rest element type must be an array type.`
This commit adapts the layout of icons in the UI to be more intuitive
and also adds some feedback on hover on some elements for clarity. Also
fixes a bug where an object line could be toggled on select.
This commit prevents the base object prototype from being unfolded when
the env of a component gets unfolded since it does not contain useful
information.
This commit ensures that search results are properly updated when the
components tree is updated so that it remains coherent if search results
appear or disappear from the view.
A few corrections to make the example more realistic:
- The component has no `error` property, so the writer probably meant `state.error`.
- The "content" slot is not being set, and "default" does the job in this case.
- The closing tag was missing in the `ErrorBoundary`'s template.
# v2.2.6
- [IMP] devtools: add svg elements detection
- [FIX] reactivity: do not notify for NOOPs on collections
- [IMP] app: export apps set as static property
- [IMP] runtime: do not check template equality outside dev mode
- [FIX] runtime: properly support t-foreach on strings
This commit extends the detection of component related dom elements to
svg elements in the page so that they can be searched and highlighted
correctly with the devtools.
Previously, if a reactive was observing the presence of an item in a
set that was originally not present, it would get notified when that
item was "deleted" from the set (even though there was nothing to delete
and the set did not change). The same applied to the key already being
present and then being "added". The same thing occured with Map, both
for presence but also for values (setting a key to the value it was
already set to would notify).
This commit fixes that.
Before this commit, Owl would export the list of apps in a global object
`__OWL_DEVTOOLS__`. However, it is sometimes useful to be able to
access that set, even outside of the devtools (for example, to register
templates in all active apps).
closes#1515
When defining a template with a name that the template set already
contains, we currently always check whether the template is the same and
throw an error when it's not. This is potentially expensive as it can
involve serializing a pretty large XML document. This check is only
supposed to help during development so this commit disables this check
outside dev mode.
Previously, support for iterables was added to t-foreach. The idea was
that anything that you can spread or on which you can use for..of would
be supported. Due to an implementation mistakes, strings, which are
iterable were not supported because we checked that the typeof the
iterable was 'object'.
To fix this, we coerce the iterable to an object and check whether that
coerced value has a Symbol.iterator property, which is what happens
behind the scenes when using for..of or spreading a primitive.
Closes: odoo/owl#1503
Firefox will not serialize the xmlns attributes of node inside
XMLDocuments, see https://bugzilla.mozilla.org/show_bug.cgi?id=175946
it will only output an xmlns attribute if the node being serialized has
a non-null namespaceURI.
When compiling templates, we currently use a special attribute,
"block-ns" to keep track of the namespace, but we do not make use of the
namespace to create the elements that will be serialized to the compiled
block string. This causes a difference in behaviour between Chrome and
Firefox: since we end up with an Element with two attributes: the
block-ns attribute and the xmlns attribute. Chrome will serialize both,
but Firefox will only serialize the block-ns because the Element does
not have a namespaceURI.
To fix this issue, we get rid of the block-ns magic attribute
completely, and use xmlns instead. We also use the namespace when
creating intermediate elements that will be used to create the
serialized block string: the namespace is only used to create the
elements but *not* set as an attribute, as this would cause chrome to
serialize it twice, causing a duplicate attribute error when parsing it
further down the line.
When creating the template element for the block at runtime, the xmlns
attribute is used both as the namespace with which to create the
element, and set as an attribute, this doesn't cause issues when
serializing later because the namespaceURI is never serialized as an
attribute when serializing an HTML document[1], so we avoid the
double-serialization in Chrome, and when doing HTML serialization,
Firefox will correctly serialize the attribute.
It's desirable that the xmlns is set as an attribute to allow users to
use owl to render SVG, and then use the HTML serialization of it as a
SVG even outside the context of an HTML document (eg to generate an SVG
file or an SVG data URL).
[1]: https://w3c.github.io/DOM-Parsing/#xml-serialization
Because the default content of a t-call is compiled before the block of
the t-call is created, the default content can sometimes incorrectly be
treated as though it is at the root of the template. This causes various
issues, in the case of a t-if, the default content will just replace the
t-call entirely when truthy, and when falsy the template will not return
anything, leading to a crash. Similar things occur with t-foreach and
t-out with a default content.
This commit fixes that by moving the block creation for the t-call ahead
of the compilation of the default content. In doing so, it makes the
context attribute "preventRoot" obsolete: the purpose of this attribute
is to somehow make the code aware that the root of the template will be
defined later, but because it wasn't propagated everywhere properly it
caused this issue. Now that the root already exists before we compile
the default content, we don't need it anymore.
This commit fixes the handling of the inspected path in the
getComponentsTree method so that it is reset when the given one does
not exist anymore. It solves a crash of the method anytime this would
happen which would prevent the tree from updating correctly.
In odoo/owl#1352 we added support for using t-foreach on Map objects,
maps should behave the same as objects where keys are available under
the name specified in t-as and values under that same name with the
suffix `_value`. Unfortunately, because the code for objects was written
in a confusing way (values were stored in a variable named `keys` and
vice versa), the implementation for Map was incorrect and keys and
values were swapped.
This commit fixes that and rewrites the code to be less confusing: keys
are extracted from the variables `k_block` and values from `v_block`,
which swaps the behaviour, and the code to prepare a list from an object
now extracts the keys in `keys` and the values in `values`
Previously, when a template failed to compile because of a syntax error
(typically because we don't do any syntax checking when compiling
expression, allowing invalid expressions to be transpiled successfully),
the error reporting was very minimal: you would only get the error
message itself (eg: "Unexpected token") with the stack information of
the error pointing to the call to `new Function` in owl, which is not
very useful.
This commit catches the compilation error and completes it with
information about the template name when available, and also adds the
generated code to the error message, allowing the user to just
copy/paste it in their web console or code editor to get a more precise
location for the error.
Before this commit, the App destroy method would not destroy everything
synchronously. The code scheduled a flush, which is asynchronous, to
finally clean up the task lists, and destroy the cancelled nodes. But
this is not really good for tests: we need a stronger guarantee that
nothing will happen after the destroy.
With this commit, we simply call the processTask method immediately
after destroying the root component, so all cancelled nodes will be
destroyed immediately.
Before this commit, the release script would build the code, then update
the version number, so the released code would have the previous release
number instead of the current.
Before this commit, the template parser would allow using t-esc on a
component tag (<MyComponent t-esc="expr"/>) but would incorrectly ignore
the component when parsing a t-out: <MyComponent t-out="expr"/> would be
parsed as <t t-out="expr"/>
This commit solves the issue, and also, moves the `t-out` parsing code
next to `t-esc` so they have the same priority relatively to other
directives.
closes#1483
# v2.2
- [IMP] runtime: add support for Map and other iterables in t-foreach
- [IMP] runtime: only destroy component in raf callback
- [REF] runtime: simplify implementation of batched
- [FIX] compiler: allow t-model.number to work with select
- [FIX] devtools: Fix crash when no root node
- [FIX] devtools: fix/imp env display
- [FIX] devtools: fix symbols handling and display
Before this commit, most of the time, components are destroyed when the
virtual dom is patched and a component node is removed. However, since
Owl is asynchronous and a component may take some time to get ready
(with onWillStart), it can happen that a component is created, then
before it is ready, it is recreated. In that case, the initial instance
has to be destroyed.
Before this commit, the destroy operation was done immediately, when we
cancel the current fibers. However, this means that we cannot have a
guarantee between micro task ticks that a component has not been
destroyed in the meantime.
For example, in Odoo, it is common to use the rpc service, which will
throw an error if called by a destroyed component. But because of the
possible destruction of a component at any time, the following code is
unsafe:
async loadSomeData() {
// guaranteed to be called when component is alive
await Promise.resolve();
// however here, component may have been destroyed
this.rpc(...)
}
So, to prevent this issue, we can slightly delay the destroy operation.
It is not entirely trivial, since we need to find a way to neutralize
the component in the meantime. But it seems like performing all that
kind of operation at the "commit" phase (so, the request animation frame
callback) makes sense to me.
So, this commit modifies the code to add a new component status
(cancelled) and use it to cancel components that are waiting to be
destroyed. These components will then be destroyed as soon as the
requestanimation frame starts, before all other dom operations.
Currently the implementation of batched is quite complicated and
difficult to read. This is because this approach tried to block all
calls at the same point and then only let the first one go through, but
an alternative approach is to simply throw away the calls that are made
after the first one has been scheduled. This change makes the
implementation much simpler to understand.
Co-authored-by: Aaron Bohy <aab@odoo.com>
Before this commit, owl parser would ignore the `.number` suffix on
<select> options. I do not see a good reason for that, and it prevents
some legitimate usecases.
closes#1444
This commit fixes a crash in the retrieval of the components tree which
happened when the first app did not contain a root node. The default
inspected component is now set to be the first root component found in
the apps.
This commit first fixes how object prototype are detected so that it
won't stop as soon as the constructor name of the object is "Object".
This allows displaying every single prototype encountered and closes
https://github.com/odoo/owl/issues/1467.
This commit also improves how the env of a component is displayed by
expanding its chain of prototypes by default while keeping its keys lit
as long as it is their first occurence in the chain.
This commit fixes the three following issues:
- Symbols could never appear in shortened display of objects
- Objects which contained only symbols as keys would be considered to be
empty and therefore not expandable
- Symbol value edition would create a new property on the object with
the stringified symbol as key instead of updating its value
closes https://github.com/odoo/owl/issues/1464
Since we now omit some observed objects when listing subscriptions, the
index of the subscription is no longer the index of the raw
subscription. This causes issue with the resolution of object paths when
they are inside a subscription. This commit fixes that by adding the
index to the raw subscription.
We also need to adapt the code that traverses the old tree, since the
index of the subscription in the component is no longer the same as the
index of the subscription "child" in the object tree.
Previously, the keys were displayed separately from the target, on top
of being displayed in bold inside the object when unfolded. This is
redundant, and in most cases you have to unfold the object anyway
because there are more keys observed than can be displayed.
The only "key" that cannot be displayed in bold inside the object is the
"key changes" magic key. This commit replaces it by a small +/- badge on
the right of the object's short content.
This commit also stops displaying observed object that are nested inside
other observed objects at the top level, as it can be confusing, and it
also heuristically gives a name to observed objects: if the observed
object is a property of the component or one of its props it will be
named accordingly, if not it will be named `[unknown]` (which may happen
when reobserving reactive objects inside a service or inside the env,
but is pretty rare in practice).
This commit changes the moment when all methods that need patching
to handle events are actually patched: the complete method of RootFiber
is now patched at devtools startup (when the tab owl tab is opened) and
every other method is patched at first activation of the event recording
functionality. This makes sure that the devtools will have no impact on
performance whatsoever when they are not directly put in use.
Class attributes are managed in a specific way in owl: they are merged
with existing class attributes, and also, they can be defined in
multiple ways (t-att-class, t-attf-class, t-att=['class', ...] and each
of these can be combined together.
Before this commit, the `t-att` syntax was handled as a normal
attribute, and therefore, would not combine as required with existing
classes.
closes#1453
This commit fixes some race conditions that were happening due to the
loading of the tree being sometimes triggered at the same moment as the
DOM patch. This would result in some destroyed component still being
present in the devtools' tree. This commit also ensures that the loading
of the tree is synchronous in regard to the component details loading.
Before this commit, the `t-on` directive assumed that the next character
would be a -, and ignored it, so if someone would write `t-onclick` by
mistake, Owl would then add an event handler on the `lick` event,
instead of `click`.
With this commit, we improve the parser to make it stricter and fail if
there is no dash.
closes#1441
This commit adds the functionnality to add components to the toggle
blacklist so that it won't be expanded by default when launching or
reloading the devtools components tree.
This commit adapts the behavior of the devtools global hook to get
the toRaw and reactive functions from the __OWL_DEVTOOLS__ variable
instead of the global owl when it is missing. Also adds the functions
to the __OWL_DEVTOOLS__ variable for the former to work.
This allows the devtools to work in environments where owl is loaded
through ES modules.
Also quickly fixes iframes detection update on page reload.
This commit fixes the key of the sub tree elements in the details
window to be 100% unique since the name of the elements could not
be unique so it would silently crash and not display. It also removes
the default size property of maps and sets since there's already a
getter available for it.
When refactoring the playground to use es-modules, a script that loads
owl as a non-module was forgotten in the head of the page and causes an
error in the console, as the script is not declared as type="module"
despite being an es-module. This script is also useless as owl is loaded
by being imported by the counter component in the page.
The importmap has also been moved to the head because of a bug in
firefox where importmaps can fail to be taken into account if they are
after a script that is not of type="module", see:
https://bugzilla.mozilla.org/show_bug.cgi?id=1833371Closes#1436
Before this commit, the translate function was not applied to text
content inside the body of a t-set (unless that content was itself
inside some html). This is not clearly not intended.
To fix it, we just need to call the translate function at the
appropriate moment.
closes#1434
Before this commit, when using a dynamic t-call, it was possible to have
a collision between subcomponent keys. This would cause hard to
understand owl crash, because owl would incorrectly use the same
component instance for two different template location. From
owl point of view, one of these component has to be destroyed and the
other one should be mounted, so a crash would occur.
The fix is to simply properly key sub components.
This commit improves how messages are relayed from the page to the
devtools by using a specific port for each instance of the owl devtools
panel and refreshing it through the heartbeat message. This allows
to prevent sending messages to every devtools instances and sorting
the messages inside.
This commit fixes how the apps methods are patched by decoupling
the patching of the destroy method from the rest because we need
to have a valid reference to a component node to do so (the root
node in this case). The previous behavior was to only patch methods
when an app with a root node is added but this was not working
when the app has no root node initially and gets one dynamically.
There is now a listener to patch the destroy method as soon as a
root node is set on any of the apps if it is not already the case.
# v2.1.2
- [FIX] t-out: does not crash when outputting null
- [FIX] playground: fix name of playground component
- [FIX] playground: use ES owl version instead of iife
- [IMP] playground: give a name so it is more explicit in devtools
- [REF] github page: move page to docs folder on master branch
- [IMP/FIX] playground: fix and improve various things
Before this commit, doing `t-out="null"` would crash, since the
safeOutput function would assume that it is a block, thanks to the fact
that `typeof null === 'object'`.
We handle here the null value just as if it was undefined: it outputs an
empty string.
Currently, some of the things on the playground must be changed directly
on the master branch while other things require checking out the
gh-pages branch and making the modifications there. Even when changes
need to be made on the master branch, all changes that are not direcly
in owl itself need to be copied by hand to the gh-pages branch.
This commit moves all files that exist on the gh-pages branch to the
master branch in the docs folder, this change will require configuring
the github page to use the docs folder instead of a separate branch, but
will make maintenance of the github page and playground easier going
forward.
The playground hasn't been touched for a while and a few things were
broken before this commit:
- Exporting as a standalone web application was broken because it still
attempted to load the templates like it was done in owl 1
- Resizing the editor tabs still tried to use `this.trigger`
While fixing the export feature, the files needed by the app were
factored out of hardcoded strings and into real files, as this makes it
easier to maintain, since these files get syntax highlighting.
The samples received the same treatment: there is now a `samples` folder
containing all the samples, it also contains a jsconfig, giving
autocompletion on owl functions while editing the files, and allowing
the owl import to look how it would when using owl as a node_module.
In the browser, this import is translated to the relative path of the
owl module using an import map.
# v2.1.1
- [FIX] components: solve missing update (concurrency issue)
- [DOC] remove old references to comp for useRef hook
- [FIX] devtools: Fix build commands for windows users
- [FIX] devtools: Fix devtools in detached window
- [IMP] devtools: Add a button to navigate to the doc
- [IMP] devtools: Add devtools documentation
- [IMP] Allow app to be mounted in shadow DOM
- [IMP] playground: allow sharing playground links
- [IMP] runtime: allow validating object values using a type description
- [FIX] devtools: Increase vertical padding of the search bar
- [FIX] devtools: Hide the collapse all button
- [FIX] devtools: Fix bad computation of highlight boxes on the page
- [FIX] package: Auto-update package-lock.json
Before this commit, in some specific situations, owl could skip updating
the DOM, even though it should. Here is what could happen:
- we have a parent component (A) and a child component (B)
- B depends on some reactive props from A
- we update some state that results in A being rendered, and B updated
- before this render is applied to the dom, we update some state again,
which causes A to be rendered again
- however, this new render is such that the B props are now identical,
so it will skip rendering B
- it will then apply the result of the render to the DOM => only A is
updated but B should also be updated!
The problem comes from the fact that Owl committed the props to the
component right after the rendering, to the new props were used in the
props comparison method to decide if B should be updated. This is
incorrect, we should always use the current props to decide what to do,
and only commit them to the component after it has been patched.
Going from owl 1.0 to 2.0, we removed the possibility of having a
reference to a component. However, the documentation was not properly
updated to reflect that.
This is useful to share editable snippets of owl code, which can be used
to paste a reproduction for a given issue or give someone an interactive
example of some feature.
In JS, objects are often used as a mapping that gives fast lookup on the
keys, in those cases, the keys themselves are not known in advance, but
the values may have a shape that they're expected to follow. This commit
adds support for a `values` key in schema for objects, which will be
checked against all values in the object during validation.
This commit adds box-sizing: border-box to the highlight elements
created by the devtools on the page. This was making some of the
boxes overflow on some pages (like on POS sessions) because
the border-box box-sizing was not the default behavior on these
pages.
This commit adds the devtools as a zip file containing both chrome
and firefox versions of the extension in the release and also updates
the doc to install it easily.
This commit adds the code of the owl devtools extension.
This extension can be added on chrome or on firefox for
developpers to be able to inspect the contents of all owl
applications that are present on any web page, see all the
components, interract with them and their content to some
extend and perform a bit of profiling with their renders.
`onWillPatch`, `onWillUnmount`, `onWillDestroy` are synchronous hooks.
Owl will not wait any promise return by the callback.
Yet, the callback return type includes `Promise<void>`,
which is confusing as one might think it means the hooks is async.
It is not really useful, but it is possible to bind an anonymous
function prop. However, with the recent change on how the bind feature
works, it now crashes.
This commit makes sure that we properly apply the `bind` operation to
the function, and not to the last term of the function.
It is common in Owl components to have anonymous function as props.
However, since each rendering create a different (but equivalent)
closure, Owl will consider the props different, so will update the child
component, but this is (often) not necessary.
This commit will help reduce the problem by introducing a new `.alike`
prop suffix, that will let Owl know that each version of that specific
prop should be considered the same, so, will be ignored by the props
comparison code.
closes#1360
Before this commit, the version number was added by the rollup
configuration script. It worked, but it meant that there was no robust
way to access the version number in many case. For example, what if I
have a page with two different version of owl? Or what if one of them
uses the ESM module system, and does not export owl as a global object?
This is not really a big deal, but accessing the version number is
useful for tooling purpose, so this commit makes it possible to read the
version number on each individual App, as a static property.
Previously, refs could get set to null incorrectly, or could stay set
when they shouldn't. This was caused by the fact that singleRefSetter
and multiRefSetters are created on every render, meaning that any render
that did not affect the status of the ref (mounted or not), would
overwrite the previous ref setter, including its closure, causing the
captured `_el` or `count` to be lost. This means that on a subsequent
render that did affect the status of the ref, the singleRefSetter would
consider that it did not set the ref, and so shouldn't unset it, causing
it to incorrectly stay keep the element, while in multiRefSetter, the
opposite problem occured: the count would be 0 on removal, causing it to
believe that it was the first call in the corresponding patch that
affected the ref, when in fact, the closure is already stale, because it
was created from the previous render, and the fresh multiRefSetter from
the latest render may already have been called by an element with that
ref that came earlier in the template.
This commit changes the ref setting strategy to work around the issue of
stale closures: we define a setRef method on ComponentNode that is
always called, this method ignores calls with `null`, meaning that the
refs object on the ComponentNode never reverts to a null value for any
key. Instead, the useRef hook will check whether the element that the
ref points to is still mounted, and return null if not.
Before this commit, the parser would remove all consecutive white spaces
for text nodes. After that, the code generator would call the translate
function with the resulting string, which then is different than what we
would expect.
With this commit, we make sure we apply the translation before removing
the additional whitespace. To do that, we have to move the code
processing the string from the parser into the code generator, which
actually makes sense, as the parser should only collect all useful
information without applying too much logic.
closes#1351
Before this commit, Owl template compiler would handle readonly
attribute as readonly properties. But this is incorrect, since the
property is actually named readOnly.
With this commit, we make sure that readonly AND readOnly are both
interpreted as the `readOnly` property. This is due to the fact that
QWeb does not discriminate between attribute and properties, so we have
to infer which is which.
closes#1362
Before this commit, properties were just handled as a special case of
attributes. But it does not make that much sense, since they are
different. For example, the `readOnly` property does not have the same
name as the `readonly` attribute. The confusion probably comes from the
fact that QWeb does not distinguish between property and attributes, so
Owl has to infer which one is which.
With this commit, we introduce a `block-property` directive in blockdom,
and change the compiler to use it in emitted code. It has the additional
benefits of slightly shrinking the runtime code, since the `isProp`
function can now be located in the compiler.
In #1161, we fixed blockdom treating 0 as falsy when patching a value,
which would result in an empty attribute. It turns out that there is
another place where we had the same fallback, which is when we compute a
dynamic attribute value, so while blockdom had the ability to set the
value to 0, when using a dynamic attribute value we would never give it
0 as a value. This commit changes the fallback strategy for dynamic
attribute values to be the same as the one in blockdom.
closes#1358
Have a t-ref on a DOM node with a t-key.
Change the t-key.
Before this commit, the old block removed its element from the component's refs *after*
the new block had been mounted, meaning that in effect, the resulting
ref at the end of the whole patch was null.
After this commit, we only remove an element from the component's ref if that very same
element was indeed the ref. (otherwise it means someone else has changed the ref.)
# v2.0.6
- [IMP] devtools: provide access to Fiber and RootFiber
- Bump shelljs and git-rev-sync
- [FIX] props validation: do not subscribe to props keys
- [FIX] reactivity: only show key in subscription if observed by callback
- [FIX] components: stop rendering stale t-component when delayed
In dev mode, owl inserts an additional props validation step. Before
this commit, the validation would iterate on all key/value pairs of each
props. If the props is reactive, this has the unfortunate side effect of
subscribing the component to each of the keys, even though it should not
be.
This commit avoids the issue by only validating the raw object.
Previously, the getSubscriptions debugging utility returned all observed
keys for a given target, instead of only the keys observed by the
callback it received as argument. This commit fixes that issues.
Previously, if the value of a t-component directive changed, but there
was already a scheduled render for the component before that change, the
rendering of the existing component would get delayed (as it should),
but after the parent's rendering was complete, the old component which
will be destroyed during the patch would still get rendered, despite
being stale. This can cause crashes if that component relies on state
that no longer exists.
This was caused by the fact that when rendering, we check the parent
chain for an active render, and also check that the component still
exists in the parent's fiber childrenMap (ie, we know that the upcoming
patch is not about to destroy the component). To do this, we use the
component's parentKey, but in the case of t-component, the parentKey for
both possible components is the same, causing the stale component to
incorrectly believe that it's not about to be destroyed, by finding the
next component in the childrenMap under the same key.
This commit fixes that by prepending the component's name to the
parentKey, meaning that if the value of the t-component changes, so will
the key, and the render will be further delayed until the new state is
patched, at which point the stale component will be destroyed and the
following attempt to render will be cancelled as expected.
The choice to use the component's name is to simplify the
implementation, this means that if using t-component and switching
between components that have the same class name, this protection will
not work. The alternative would be to generate a unique id for the
component class, eg with a WeakMap, but this both complicates the
implementation and adds runtime overhead, for a case that is likely
extremely rare. We are open to refine the implementation should this
problem occur in practice.
Previously, when having a long lived reactive object and writing a lot
of keys to it, clearing the reactive subscriptions on that object would
slow down as time went on. This is because when clearing a target's
subsctiptions for a callback, we look at all the keys that are observed, and for all
the observed keys, we remove the callback from the set of callbacks
observing that key. The problem arises from the fact that after doing
that, even if the set of callbacks observing that key is now empty, we
don't remove the key from the observed keys, meaning that any further
clearing of subscriptions will have to iterate over that key to attempt
to clear the callbacks even if there are none.
This commit fixes this problem by simply removing the key from the
observedKeys if there are no longer any callbacks observing it.
This commit also improves performance for bare reactives (reactives
created with no callback). The initial implementation simply creates a
default empty callback when creating a reactive, and treats it like any
other, meaning it can observe keys and be notified of changes even
though we know in advance that it does nothing. This can compound with
the previous issue when you're doing a lot of manipulations on a bare
reactive internally for the sole purpose of notifying outside observers,
as this creates a lot of useless work in the reactivity system.
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.
2022-09-06 12:06:59 +02:00
331 changed files with 51612 additions and 8679 deletions
It is common to have the need to pass a callback as a prop. Since Owl components
@@ -95,6 +137,31 @@ class SomeComponent extends Component {
}
```
The `.bind` suffix also implies `.alike`, so these props will not cause additional
renderings.
## Translatable props
When you need to pass a user-facing string to a subcomponent, you likely want it
to be translated. Unfortunately, because props are arbitrary expressions, it wouldn't
be practical for Owl to find out which parts of the expression are strings and translate
them, and it also makes it difficult for tooling to extract these strings to generate
terms to translate. While you can work around this issue by doing the translation in
JavaScript, or by using `t-set` with a body (the body of `t-set` is translated),
and passing the variable as a prop, this is a sufficiently common use case that Owl
provides a suffix for this purpose: `.translate`.
```xml
<tt-name="ParentComponent">
<ChildsomeProp.translate="some message"/>
</t>
```
Note that the content of this attribute is _NOT_ treated as a JavaScript expression:
it is treated as a string, as if it was an attribute on an HTML element, and translated
before being passed to the component. If you need to interpolate some data into the
string, you will still have to do this in JavaScript.
## Dynamic Props
The `t-props` directive can be used to specify totally dynamic props:
@@ -167,6 +234,7 @@ For each key, a `prop` definition is either a boolean, a constructor, a list of
-`type`: the main type of the prop being validated
-`element`: if the type was `Array`, then the `element` key describes the type of each element in the array. If it is not set, then we only validate the array, not its elements,
-`shape`: if the type was `Object`, then the `shape` key describes the interface of the object. If it is not set, then we only validate the object, not its elements,
-`values`: if the type was `Object`, then the `values` key describes the interface of values in the object, this allows validating objects that are used as mappings, where keys are not known in advance but the shape of the values is.
-`validate`: this is a function which should return a boolean to determine if
the value is valid or not. Useful for custom validation logic.
-`optional`: if true, the prop is not mandatory
@@ -192,7 +260,7 @@ class ComponentB extends owl.Component {
@@ -9,6 +9,7 @@ functions are all available in the `owl.utils` namespace.
- [`loadFile`](#loadfile): loading a file (useful for templates)
- [`EventBus`](#eventbus): a simple EventBus
- [`validate`](#validate): a validation function
- [`batched`](#batched): batch function calls
## `whenReady`
@@ -78,3 +79,22 @@ validate(
// - 'id' is missing (should be a number),
// - 'url' is missing (should be a boolean or list of numbers),
```
## `batched`
The `batched` function creates a batched version of a callback so that multiple calls to it within the same microtick will only result in a single invocation of the original callback.
```js
functionhello(){
console.log("hello");
}
constbatchedHello=batched(hello);
batchedHello();
// Nothing is logged
batchedHello();
// Still not logged
awaitPromise.resolve();// Await the next microtick
<p>Templates are based on the XML format, which allows interesting applications. For example, they could be stored in a database and modified dynamically with <tt>xpaths</tt>.</p>
</section>
<sectionclass="column">
<h3>Templates compiled in the browser</h3>
<p>This may not be a good fit for all applications, but if you need to generate dynamically user interfaces in the browser, this is very powerful. For example, a generic form view component could generate a specific form user interface for each various models, from a XML view.</p>
</section>
<sectionclass="column">
<h3>No toolchain required</h3>
<p>This is extremely useful for some applications, if, for various reasons (security/deployment/dynamic modules/specific assets tools), it is not ok to use standard web tools based on <tt>npm</tt>.</p>
</section>
</div>
</div>
</article>
<articleclass="example">
<divclass="container">
<h2>Example</h2>
<divclass="row">
<sectionclass="column">
<pre><codeclass="javascript"></code></pre>
</section>
<sectionclass="column">
<pre><codeclass="xml"></code></pre>
<divid="app-container"></div>
</section>
</div>
</div>
</article>
</main>
<footer>
<p><ahref=".">OWL</a> is licensed under LGPLv3.<br>Logo from <ahref="https://github.com/googlefonts/noto-emoji">Google Noto Emoji Font</a>, licensed under Apache License 2.0</p>
/* auto, since non-WebKit browsers doesn't support input styling */
height:auto;
position:absolute;
top:0;
bottom:0;
margin:auto0;
border:none;/* Mobile Safari */
-webkit-appearance:none;
appearance:none;
}
.todo-listli.toggle{
opacity:0;
}
.todo-listli.toggle+label{
/*
Firefox requires \`#\` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
IE and Edge requires *everything* to be escaped to render, so we do that instead of just the \`#\` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
<aclass="btn flash"t-on-click="downloadCode"title="Download a Zip with this Code"><iclass="fas fa-download"></i></a>
<aclass="btn flash"t-on-click="exportStandaloneApp"title="Download zip that bundles this app with a tiny python web server"><iclass="fas fa-download"></i></a>
<aclass="btn flash share-code"t-on-click="shareCode"title="Copy a link to this playground"><span>Share</span><spanclass="copied-notification"t-if="state.copied">Copied link to clipboard!</span></a>
<p>Note: these examples are using recent features of Javascript, and require a recent browser to work without a transpilation step!
For example, it makes use of class fields and class static fields. These examples should work in a recent Chrome version.
<p>
Note: these examples make use of recent features of Javascript, and require a recent browser to work without a transpilation step! Among other things, it uses class fields and import maps. If you encounter issues, make sure your browser is up to date.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
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.