# Component Decorators
Ovee.js uses ECMAScript decorators quite heavily. Despite this JS feature it's still in a proposal state, we've decided to utilize it as it brings amazing clarity to component code. You can find out more about ES decorators notation here.
# @register
Adds static method getName()
that returns name of a component. This method is required for a component to be registered.
Parameters:
name: string
- name of component. Must be a two word name, either inPascalCase
,camelCase
orkebab-case
Example:
@register('my-component')
class MyComponent extends Component {}
# @el
Binds element found in children of this.$element
with selector
passed to querySelector
or querySelectorAll
. If DOM structure in this.$element
changes in any way, the reference is updated.
Parameters:
selector: string
- selector passed further toquerySelector
orquerySelectorAll
options?: { list?: boolean }
- optional object with propertylist
. If set totrue
,querySelectorAll
will be used andNodeList
would be returned.
Example:
@register('my-component')
class MyComponent extends Component {
@el('.counter__button')
button;
@el('.slide', { list: true })
slides;
}
# @bind
Bind method as a callback for specified event listeners. Automaticlly removes them on destroy
.
Parameters:
events: string
- string of space separated events to listen tooptions?: ListenerOptions
- optional config object with the same parameters as$on
. We can change target for event listener or passaddEventListener
config options
Example:
@register('my-component')
class MyComponent extends Component {
@bind('mousein mouseout')
onMouseChange() {
// ...
}
@bind('click', { target: '.base__submit' })
onSubmit() {
// ...
}
@bind('resize', { target: window })
onResize() {
// ...
}
@bind('click', { target: '.close-button', root: true })
onCloseDialog() {
// ...
}
@bind('scroll', { target: window, passive: true })
onScroll() {
// ...
}
@bind('click', { target: '.list__item', multiple: true })
onLickItemClicked() {
// ...
}
}
# @reactive
Makes property reactive by using makeReactive
.
Example:
@register('my-component')
class MyComponent extends Component {
@reactive()
counter;
}
# @prop
Adds one way binding from root element (this.$element
) property to class instance property. Any changes made to this elements property value, will change instance property, but not the other way around. That makes this property readonly.
Parameters:
propName?: string
- optional name of element's property. If not specified, class instance property name is used.
Example:
@register('my-component')
class MyComponent extends Component {
@prop()
scrollHeight;
@prop('className')
class;
init() {
if (!this.class.includes('is-open')) {
this.$element.classList.add('is-open')
}
}
}
# @dataParam
Just like @prop
, adds one way binding, but on data-...
properties of root element. Any changes made to this elements data value under specified key, will change instance property, but not the other way around. That makes this property readonly.
Parameters:
propName?: string
- optional name of element's property. If not specified, class instance property name is used.
Example:
@register('my-component')
class MyComponent extends Component {
@dataParam()
initial; // will bind to data-initial
@dataParam('loaded')
isLoaded; // will bind to data-loaded
}
# @watch
Watch given path and call method that decorates, when value under given path change. Can only watch properties, that are marked as reactive. Uses doWatch
underneath.
Parameters:
path: string | WatchSource | Array<WatchSource | object>
- source to watch. If source is path, than it's path to watch underthis
, f.ex.:counter
,obj.a
. Otherwise, it needs to be either:ref
ormakeComputed
instance,function
that returns reactive value like:() => data.test
, or multisource asarray
of previous options. More on that indoWatch
options?: { immediate?: boolean }
- optional object with propertyimmediate
. When set totrue
, decorated method will be called immediatly after init. In other case, it will be called only after watched value changes.
Example:
@register('my-component')
class MyComponent extends Component {
@reactive()
counter = 0
@reactive()
data = { name: 'test name' }
@watch('counter')
onCounterChange(newVal, oldVal, path) {
// do something on change
}
@watch('data.name', { immediate: true })
onNameChange() {
// do something on change
}
}
# @watchEffect
Works almost the same way as @watch
, but don't require from you to specify what sources to watch for.
If you reference reactive value inside, @watchEffect
will catch it and remember, so it's actually much more pleasent to use than classic @watch
.
Also, in opposite to @watch
, @watchEffect
will run almost immediately after declaration as it needs to catch initial reactive references. Uses doWatchEffect
underneath.
Example:
@register('my-component')
class MyComponent extends Component {
@reactive()
counter = 0
counterText = ''
@watchEffect()
updateCounterText() {
this.counterText = `Current counter value: ${this.counter}`;
this.$element.innerHTML = this.counterText;
}
init() {
setInterval(() => {
// this will make the counter update every 1s
// and will cause our innerHTML to update as well
this.counter += 1;
}, 1000)
}
}
# @computed
Creates a reactive function that is lazy and caches it's last result. It also works when reactive objects and reevaluates only if any reactive reference inside was changed.
It's the same concept and implementation as Vue's computed
.
This decorator can only be used on a get
accessor.
Example:
import { ref } from 'ovee.js'
// we can use external reactive variable
const counter = ref(0)
@register('my-component')
class MyComponent extends Component {
multiplier = 2
@computed()
get multipliedCounter() {
return counter.value * this.multiplier;
}
init() {
// counter: 0, multiplier: 2
console.log(this.multipliedCounter) // 0
counter.value += 1;
// counter: 1, multiplier: 2
console.log(this.multipliedCounter) // 2
this.multiplier = 4;
// we changed multiplier value, but computed is not reevaluated,
// because multiplier is not reactive
// counter: 1, multiplier: 4
console.log(this.multipliedCounter) // 2
counter.value += 1
// counter: 2, multiplier: 4
console.log(this.multipliedCounter) // 8
}
}
Computed properties can freely be used inside TemplateComponent's
template
method and will trigger rerender.
# @module
This decorator is a simple tool for retrieving any registered module instance. It uses $app.getModule
under the hood. Similar to @el
, it should be used on a class field. It accepts either module's name, or the module itself. The second way is suggested, as it's independent of the possibility of changing the module's name.
Example:
import { module, Component, register } from 'ovee.js'
@register('my-component')
class MyComponent extends Component {
@module('MyModule')
myModule;
@module(OtherModule)
otherModule;
}
If you're using TypeScript
, you should also type the field with module you would receive. But you can omit it, if in your tsconfig.json
you have flag emitDecoratorMetadata
set to true.
{
"compilerOptions": {
// ...
"emitDecoratorMetadata": true,
// ...
},
// ...
}
Example:
import { module, Component, register } from 'ovee.js'
import { MyModule } from '../modules/MyModule'
import { OtherModule } from '../modules/OtherModule'
@register('my-component')
class MyComponent extends Component {
// with `emitDecoratorMetadata: false`
@module(MyModule)
myModule: MyModule;
// with `emitDecoratorMetadata: true`
@module()
otherModule: OtherModule;
}
← Components Modules →