2019-11-11 17:31:34 +01:00
# 🦉 Props 🦉
## Content
- [Overview ](#overview )
- [Definition ](#definition )
2022-01-13 10:26:14 +01:00
- [Binding function props ](#binding-function-props )
2019-11-11 17:31:34 +01:00
- [Dynamic Props ](#dynamic-props )
2022-01-13 10:26:14 +01:00
- [Default Props ](#default-props )
- [Props validation ](#props-validation )
- [Good Practices ](#good-practices )
2019-11-11 17:31:34 +01:00
## Overview
In Owl, `props` (short for _ properties _ ) is an object which contains every piece
of data given to a component by its parent.
``` js
class Child extends Component {
static template = xml ` <div><t t-esc="props.a"/><t t-esc="props.b"/></div> ` ;
}
class Parent extends Component {
2020-10-13 20:38:37 +08:00
static template = xml ` <div><Child a="state.a" b="'string'"/></div> ` ;
2019-11-11 17:31:34 +01:00
static components = { Child } ;
state = useState ( { a : "fromparent" } ) ;
}
```
In this example, the `Child` component receives two props from its parent: `a`
and `b` . They are collected into a `props` object by Owl, with each value being
evaluated in the context of the parent. So, `props.a` is equal to `'fromparent'` and
`props.b` is equal to `'string'` .
Note that `props` is an object that only makes sense from the perspective of the
child component.
## Definition
The `props` object is made of every attributes defined on the template, with the
following exceptions:
- every attribute starting with `t-` are not props (they are QWeb directives),
In the following example:
``` xml
<div >
<ComponentA a= "state.a" b= "'string'" />
<ComponentB t-if= "state.flag" model= "model" />
</div>
```
the `props` object contains the following keys:
- for `ComponentA` : `a` and `b` ,
- for `ComponentB` : `model` ,
2022-01-13 10:26:14 +01:00
## Binding function props
2019-11-11 17:31:34 +01:00
2022-01-13 10:26:14 +01:00
It is common to have the need to pass a callback as a prop. Since Owl components
are class based, the callback frequently needs to be bound to its owner component.
So, one can do this:
2019-11-11 17:31:34 +01:00
``` js
2022-01-13 10:26:14 +01:00
class SomeComponent extends Component {
static template = xml `
<div>
<Child callback="doSomething"/>
</div> ` ;
setup ( ) {
this . doSomething = this . doSomething . bind ( this ) ;
}
doSomething ( ) {
// ...
2019-11-11 17:31:34 +01:00
}
}
```
2022-01-13 10:26:14 +01:00
However, this is such a common use case that Owl provides a special suffix to do
just that: `.bind` . This looks like this:
2019-11-11 17:31:34 +01:00
2022-01-13 10:26:14 +01:00
``` js
class SomeComponent extends Component {
static template = xml `
<div>
<Child callback.bind="doSomething"/>
</div> ` ;
doSomething ( ) {
// ...
}
}
```
2019-11-11 17:31:34 +01:00
## Dynamic Props
The `t-props` directive can be used to specify totally dynamic props:
``` xml
<div t-name= "ParentComponent" >
<Child t-props= "some.obj" />
</div>
```
``` js
class ParentComponent {
static components = { Child } ;
some = { obj : { a : 1 , b : 2 } } ;
}
```
2022-01-13 10:26:14 +01:00
## Default Props
If the static `defaultProps` property is defined, it will be used to complete
props received by the parent, if missing.
``` js
class Counter extends owl . Component {
static defaultProps = {
initialValue : 0 ,
} ;
...
}
```
In the example above, the `initialValue` props is now by default set to 0.
## Props Validation
As an application becomes complex, it may be quite unsafe to define props in an informal way. This leads to two issues:
- hard to tell how a component should be used, by looking at its code.
- unsafe, it is easy to send wrong props into a component, either by refactoring a component, or one of its parents.
A props type system solves both issues, by describing the types and shapes
of the props. Here is how it works in Owl:
- `props` key is a static key (so, different from `this.props` in a component instance)
- it is optional: it is ok for a component to not define a `props` key.
- props are validated whenever a component is created/updated
- props are only validated in `dev` mode (see [how to configure an app ](app.md#configuration ))
- if a key does not match the description, an error is thrown
- it validates keys defined in (static) `props` . Additional keys given by the
2022-01-19 10:40:15 +01:00
parent will cause an error (unless the special prop `*` is present).
2022-01-13 10:26:14 +01:00
- it is an object or a list of strings
- a list of strings is a simplified props definition, which only lists the name
of the props. Also, if the name ends with `?` , it is considered optional.
- all props are by default required, unless they are defined with `optional: true`
2022-01-19 10:40:15 +01:00
(in that case, it is only done if there is a value)
2022-01-13 10:26:14 +01:00
- valid types are: `Number, String, Boolean, Object, Array, Date, Function` , and all
constructor functions (so, if you have a `Person` class, it can be used as a type)
- arrays are homogeneous (all elements have the same type/shape)
For each key, a `prop` definition is either a boolean, a constructor, a list of constructors, or an object:
- a boolean: indicate that the props exists, and is mandatory.
- a constructor: this should describe the type, for example: `id: Number` describe
the props `id` as a number
2022-06-14 18:05:58 +02:00
- an object describing a value as type. This is done by using the `value` key. For example, `{value: false}` specifies that the corresponding value should be equal to false.
2022-01-13 10:26:14 +01:00
- a list of constructors. In that case, this means that we allow more than one
type. For example, `id: [Number, String]` means that `id` can be either a string
or a number.
- an object. This makes it possible to have more expressive definition. The following sub keys are then allowed (but not mandatory):
- `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,
- `validate` : this is a function which should return a boolean to determine if
the value is valid or not. Useful for custom validation logic.
2022-02-04 09:36:54 +01:00
- `optional` : if true, the prop is not mandatory
2022-01-13 10:26:14 +01:00
2022-01-19 10:40:15 +01:00
There is a special `*` prop that means that additional prop are allowed. This is
sometimes useful for generic components that will propagate some or all their
props to their child components.
2022-02-04 09:36:54 +01:00
Note that default values cannot be defined for a mandatory props. Doing so will
result in a prop validation error.
2022-01-13 10:26:14 +01:00
Examples:
2022-01-19 10:40:15 +01:00
``` js
class ComponentA extends owl . Component {
static props = [ 'id' , 'url' ] ;
...
}
class ComponentB extends owl . Component {
static props = {
count : { type : Number } ,
messages : {
type : Array ,
element : { type : Object , shape : { id : Boolean , text : String }
} ,
date : Date ,
2022-02-04 09:36:54 +01:00
combinedVal : [ Number , Boolean ] ,
optionalProp : { type : Number , optional : true }
2022-01-19 10:40:15 +01:00
} ;
...
}
```
2022-01-13 10:26:14 +01:00
``` js
// only the existence of those 3 keys is documented
static props = [ 'message' , 'id' , 'date' ] ;
```
2022-01-19 10:40:15 +01:00
``` js
// only the existence of those 3 keys is documented. any other key is allowed.
static props = [ 'message' , 'id' , 'date' , '*' ] ;
```
2022-01-13 10:26:14 +01:00
``` js
// size is optional
static props = [ 'message' , 'size?' ] ;
```
``` js
static props = {
messageIds : { type : Array , element : Number } , // list of number
otherArr : { type : Array } , // just array. no validation is made on sub elements
otherArr2 : Array , // same as otherArr
someObj : { type : Object } , // just an object, no internal validation
someObj2 : {
type : Object ,
shape : {
id : Number ,
name : { type : String , optional : true } ,
url : String
] } , // object, with keys id (number), name (string, optional) and url (string)
someFlag : Boolean , // a boolean, mandatory (even if `false`)
someVal : [ Boolean , Date ] , // either a boolean or a date
otherValue : true , // indicates that it is a prop
kindofsmallnumber : {
type : Number ,
validate : n => ( 0 <= n && n <= 10 )
} ,
size : {
validate : e => [ "small" , "medium" , "large" ] . includes ( e )
} ,
2022-06-14 18:05:58 +02:00
someId : [ Number , { value : false } ] , // either a number or false
2022-01-13 10:26:14 +01:00
} ;
```
2022-05-22 10:44:09 +02:00
Note: the props validation code is done by using the [validate utility function ](utils.md#validate ).
2022-01-13 10:26:14 +01:00
## Good Practices
A `props` object is a collection of values that come from the parent. As such,
they are owned by the parent, and should never be modified by the child:
``` js
class MyComponent extends Component {
constructor ( parent , props ) {
super ( parent , props ) ;
props . a . b = 43 ; // Never do that!!!
}
}
```
Props should be considered readonly, from the perspective of the child component.
If there is a need to modify them, then the request to update them should be
sent to the parent (for example, with an event).
Any value can go in a props. Strings, objects, classes, or even callbacks could
be given to a child component (but then, in the case of callbacks, communicating
with events seems more appropriate).