Skip to main content

Custom directives

Custom directives are classes that manipulates an element in our MonsterJS application.

Creating a custom directive

To create a custom directive, we can use the cli to automatically generate a directive file with boilerplate codes or we can manually create a file and write the code from scratch.

The following code is an example of a working directive codes but without functionality yet.

import { directive, AllDirectivesArg } from '@monster-js/core';

export function highlight(arg: AllDirectivesArg) {
}

directive(highlight, 'highlight')

The next step is to handle the directive by creating a condition to check if the directive exists.

Here is an example on how to handle the directive highlight:color="red".

import { directive, AllDirectivesArg } from '@monster-js/core';

export function highlight(arg: AllDirectiveArg) {
const { color } = arg.directives;

if (color) {
arg.element.style.backgroundColor = color.get();
}
}

directive(highlight, 'highlight');

Register a directive

Before we can use a custom directive we need to register it to a module or component.

The following are examples on how to register a directive.

In component

import { component, directives } from '@monster-js/core';
import { customDirective1 } from './custom-directive1.directive';
import { customDirective2 } from './custom-directive2.directive';

export function greeting() {
return <h1>Hello World!</h1>
}

component(greeting, 'app-greeting');
directives(greeting, customDirective1, customDirective2);

In module

import { Module } from '@monster-js/core/module';
import { customDirective1 } from './custom-directive1.directive';
import { customDirective2 } from './custom-directive2.directive';

export const AppModule: Module = {
directives: [customDirective1, customDirective2]
};

Using a directive

After the custom directive is registered, we can now use it like the built-in directives using the following syntax: <namespace>:<name>="<value>".

import { component, directives } from '@monster-js/core';
import { highlight } from './highlight.directive';

export function greeting() {
return <h1 highlight:color="red">Hello World!</h1>
}

component(greeting, 'app-greeting');
directives(greeting, highlight);

Value change watcher

To let our directive react when it's value is changed we can use the watchDirective(arg, updateCallback) function from the core package.

ParamsDescription
argThe directive to be watched.
updateCallbackA function that runs when the value of the directive is changed.

Here's an example on how to change the background color of an element based on the value passed to its directive.

The directive

import { directive, watchDirective, AllDirectivesArg } from '@monster-js/core';

export function highlight(arg: AllDirectivesArg) {
const { color } = arg.directives;

if (color) {
const initialValue = arg.directive.get();
arg.element.style.backgroundColor = initialValue;

watchDirective({
element: arg.element,
component: arg.component,
directive: color
}, value => {
arg.element.style.backgroundColor = value;
});
}
}

directive(highlight, 'highlight')

The component

import { component, directives } from '@monster-js/core';
import { highlight } from './highlight.directive';

export function greeting() {

highlightColor = 'red';

render() {
return <h1 highlight:color={this.highlightColor}>Hello World</h1>
}
}

component(greeting, 'app-greeting');
directives(greeting, highlight);

We can also use the watch(valueCaller, element, component, updateCallback) function for a better control of our watcher.

ParamsDescription
valueCallerA function that calls the value of the directive.
elementThe element where the directive is attached.
componentThe component where the directive is being used.
updateCallbackA function that runs when the value of the directive is changed.

Example.

import { directive, watch, AllDirectivesArg } from '@monster-js/core';

export function highlight(arg: AllDirectivesArg) {
const { color } = arg.directives;
if (color) {
const initialValue = color.get();
arg.element.style.backgroundColor = initialValue;

watch(() => color.get(), arg.element, arg.component, value => {
arg.element.style.backgroundColor = value;
});
}
}

directive(highlight, 'highlight');

Handling all directive names

In some cases we need to handle any names passed on our directive.

A good example for this is the props directive that accepts all directive names. Example. prop:user={this.user} or prop:post={this.post} where user and post are the directive names.

To catch all the directive names we can just loop through the arg.directives property of the directive function parameter.

Example.

The component

import { component, directives } from '@monster-js/core';
import { animalDirective } from './animal.directive';

export function animals() {
return <h1
animal:cat="meow meow meow"
animal:chicken="cluck cluck cluck"
>Animal sounds</h1>
}

component(animals, 'app-animals');
directives(animals, animalDirective);

The directive

import { directive, AllDirectivesArg } from '@monster-js/core';

export function animalDirective(arg: AllDirectivesArg) {

Object.keys(arg.directives).forEach(key => {
console.log(`animal: ${key}, sound: ${arg.directives[key].get()}`);
});

}

directive(animalDirective, 'animal')

The example above will log the following:

'animal: cat, sound: meow meow meow'
'animal: chicken, sound: cluck cluck cluck'