Valyrian.js 5.0.8

Get started - Custom directives

What is Valyrian.js?InstallationHyperscript/JSXComponentsLifecycle methodsDirectivesServer side jsxPlugins

Custom directives

Valyrian.js directives can be created calling the method v.directive passing the name of the directive as first parameter and a custom method as the second parameter.

Simple directive

The custom method will get the directive value as first parameter.
v.directive('test', (value) => console.log(`Hello ${value}`));
v.mount('body', () => <div v-test="world">div>);
$ Hello world

Getting vnodes

The custom method will get the new/updated vnode as the second parameter and the old vnode as the third.
v.directive('test2', (v, vnode, oldVnode) => console.log(vnode, oldVnode));
v.mount('body', () => <div v-test2>Hellodiv>)
Hello
$ {
  props: { 'v-test2': true },
  children: [ 'Hello' ],
  name: 'div',
  isSVG: false,
  dom: Element {...}
} undefined

Identify created or updated node

We can identify if it is the first render by testing the presence of the third parameter (the old vnode). If there is no old vnode, then we can be confident that this is the first render.
v.directive('identify-first-render', (v, vnode, oldVnode) => {
  if (!oldVnode) {
    vnode.children = 'First render, vnode created';
  } else {
    vnode.children = 'Second render, vnode updated';
  }
});

v.mount('body', () => <div v-identify-first-render>div>);
First render, vnode created

Modifying children of vnodes

As in the previous sample, you can update or overwrite the children of this node modifying the children attribute of the vnode.
v.directive('modify-children', (v, vnode) => vnode.children = 'Hello world');

v.mount('body', () => <div v-modify-children>Hello John Doediv>);
Hello world

Modify properties of vnodes

Modify properties is not guaranteed because the properties are processed by place.
If the directive needs to update previous properties you need to update the property using the v.updateProperty method.
// This will update the property by place, the change is not guaranteed.
v.directive('modify-property', (value, vnode) => vnode.props.class = 'bg-success');

// This will update of the property by method, place is not important and the change is guaranteed.
v.directive('modify-property-by-method', (value, vnode) => {
  vnode.props.class = 'bg-success';
  v.updateProperty('class', vnode);
});

v.mount('body', () => [
  <button class="bg-primary" v-modify-property>class property before directivebutton>,
  <br />,
  <button v-modify-property class="bg-primary">class property after directivebutton>,
  <br />,
  <button class="bg-primary" v-modify-property-by-method>class before directive and updated by methodbutton>
]);


Adding events

Adding events to dom

To add an event to the dom you can simply add the on${eventName} attribute to the vnode you want to use it.
v.mount('body', () => <button class="border-primary" onclick={(event, dom) => dom.innerText = `Event ${event.type} fired`}>Simple buttonbutton>);
Each fired event will trigger an update of the app. To prevent this behavior you need to cancel the event with event.preventDefault().

Adding events to vnode

Although you can add a listener directly to the dom property of the vnode, it is a best practice to use the v.updateProperty method to let Valyrian.js handle the add and update of the element without worry to remove the listener if the dom updates or is removed form the dom.
v.directive('click', (value, vnode) => {
  vnode.props.onclick = (event, dom) => dom.innerText = `Event ${event.type} fired`;
  v.updateProperty('onclick', vnode);
});
v.mount('body', () => <button v-click>Simple buttonbutton>);

Adding events to other dom and leaning events (v.onCleanup)

If you want to add a listener to another dom element from within a directive you will need to remove the handler on each render using the v.onCleanup method.
This method runs the passed callback at the begining of each render.
Also you need to validate first if we are in the browser scope because in nodejs some dom methods don't exists and events can not be fired.
v.directive('listen-to-scroll', (value, vnode) => {
  // We check if we are not in the browser
  if (!v.isNode) {
    // Get the article element
    let article = document.getElementsByTagName('article')[0];

    // Set the listener to a var
    let listener = (e) => vnode.dom.innerText = article.scrollTop;

    // Attach the listener
    article.addEventListener('scroll', listener);

    // If we re-render the ui remove the listener before attach it again
    v.onCleanup(() => article.removeEventListener('scroll', listener));
  }
});

v.mount('body', () => <span v-listen-to-scroll>span>);

Flag implementation example

We don't have flags as vue or ember but for this we can use a directive as flag.
let dayjs = require('dayjs');

let formatDate = (value) => dayjs(value).format('MMMM D, YYYY');

v.directive('date-inline', (date, vnode) => vnode.children = formatDate(date));
v.directive('date', (value, vnode) => vnode.children = formatDate(vnode.children[0]));

v.mount('body', () => [
  <div v-date-inline="08-16-2018">div>,
  <div v-date>08-16-2018div>
]);
August 16, 2018
August 16, 2018

Complex example v-switch

This complex example shows the capabilities of Valyrian directives.
It works as a switch statement and needs a set of arrays as children of the form [testCase|method, vnodes|method]
v.directive('switch', (value, vnode) => {
  for (let i = 0, l = vnode.children.length; i < l; i++) {
    let [test, handler] = vnode.children[i];
    let result = typeof test === 'function' ?
      test(value) :
      value === test;

    if (result) {
      vnode.children = typeof handler === 'function' ? handler(value) : handler;
      return;
    }
  }

  vnode.children = value;
});

let Component = ({name}) => <div v-switch={name}>
  {['John', <span>Hello Johnspan>]}
  {[(val) => val === 'John Doe', <span>Hello John Doespan>]}
  {['Jane', (val) => <span>Hello {val} Doespan>]}
div>;

v.mount('body', () => [
  <ComplexExample name='John' />,
  <ComplexExample name='John Doe' />,
  <ComplexExample name='Jane' />
]);
Hello John
Hello John Doe
Hello Jane Doe