Directives are special HTML attributes with the d3-
prefix.
They apply special reactive behavior to the hosting HTML element.
Differently from components, a directive doesn't replace
the HTML element they belong to.
Directive attribute values are one of
- empty
- binding expressions
- JSON string
A directive attribute value can be a non binding expression provided the create method
returns nothing and set the active
attribute to true
.
The library provides several directives for every day task.
For example the d3-html
directive binds an expression to the inner
Html of the element containing the directive:
<div id="entry">
...
<p d3-html='paragraph'></p>
...
</div>
Here the paragraph
is a reactive attribute of the View model.
d3.view({
model: {
paragraph: 'TODO'
}
}).mount('#entry');
The d3-attr directive creates a one-way binding between a model property and an HTML element attribute
<input d3-attr-name="code" d3-attr-placeholder="description || code">
The attr
can be omitted for class
, name
, disabled
,
readonly
and required
.
<input d3-name="code" d3-class="bright ? 'bright' : 'dull'">
code
and bright
are reactive properties of the view-model.
The d3-class
directive can accept array values too.
As the name suggest, the d3-for directive can be used to repeat the
element once per item from a collection. Each element gets its own model,
where the given loop variable is set to the current collection item,
and index
is set to the item index or key.
<li d3-for="item in items">
<a d3-attr-href="item.href" d3-html="item.label"></a>
</li>
The d3-html directive creates a one-way-binding between a model property
and the innerHtml property of the hosting HTML element.
You can use it to attach html or text to element dynamically.
Add data-transition-duration
to your element to allow transitions:
<p d3-html="message" data-transition-duration="250"></p>
The d3-if directive displays or hides an element depending on the binding expression.
The display style is preserved. Add data-transition-duration
to your element
to allow transitions:
<div d3-if="showMe" data-transition-duration="250">...</div>
The d3-on directive attaches an event listener to the element.
The event type is denoted by the argument postfix, d3-click-<arg>
.
if the arg
part is omitted it is assumed to be a click
event.
The expression should be a model method call and it is the event callback
.
The event callback
listens to native DOM events only.
<button d3-on-click="submit()">Submit</button>
The d3.event object is available in the model context as $event
and can be passed to the callback function. For example
<button d3-on-click="submit($event)">Submit</button>
The d3-prop directive creates a one only binding between an expression and a prop
for a component.
Lets say we have a component which requires a msg
property do display a its inner html:
const cm = view({
model: {
message: "this is a test"
},
components: {
hi (props) {
return `<p>${p.msg}</p>`;
}
}
});
One can pass the msg
from the model with the d3-prop
directive
<hi d3-prop-msg="message"></hi>
The d3-value directive establish a two-way data binding for HTML elements supporting the value property. The binding is two ways because
- an update in the model attribute causes an update in the HTML value property
- an update in the HTML value property causes an update in the model attribute
Creating a custom directive involve the following steps:
- Create a (reusable) directive object:
var mydir = {
create (expression) {
return expression;
},
mount (model) {
return model;
},
refresh (model, value) {
},
destroy (model) {
}
};
- Add the directive to the view constructor:
var vm = d3.view({
el: '#entry',
directives: {
mydir: mydir
}
});
- Use the directive via the
d3-mydir
attribute.
A directive is customized via the four methods highlighted above. None of the methods need implementing, and indeed for some directive the refresh method is the only one which needs attention.
Directives can also be added via plugins.
The create
method is called once only, at the end of directive initialisation, no binding with the HTML element or model has yet occurred.
The expression
is the attribute value, a string, and it is not yet parsed.
The returned value of this method is important in defining what the directive does. There are three alternatives
- The default implementation simply returns the expression
create (expression) {
return expression;
}
In this case the directive is considered active
, and if the expression is a non empty string it is considered a binding expressions.
- It is possible to return a different expression, for example
create (expression) {
return 'calculate(' + expression + ')';
}
This case is very much equivalent to the first case, with the return value considered a binding expression.
- To avoid the creation of a binding expression, the create method should return nothing and set the
active
attribute totrue
.
create (expression) {
this.target = expression;
this.active = true;
}
- If the
active
attribute is not set totrue
and the return value is empty and the input expression is not an empty string, the directive is not executed (active
is set to false by the framework).
The mount
method is called once only, at the beginning of the binding process with the HTML element.
If an expression is returned by the create
method, the expression is now parsed and available in the this.expression
attribute.
This method must return the model for binding (it doesn't need to be the same as the input model, but usually it is). However, if it returns nothing, the binding execution is aborted. The default implementation simply returns the input model.
mount (model) {
return model;
}
The input model belongs to the ancestor component.
This method can be used to add additional binding to the model
for example.
This method is called every time the model associated with the element hosting the directive, has changed value. It is also called at the end of a successful mount. The input model
is the one returned by the mount
method.
Called once only when the element hosting the directive is removed from the DOM.
When the directive has been removed, the destroyDirective
custom event is emitted by the model.
A directive has all the properties and methods of the viewBase prototype with these additional properties.
true
when the directive is active
The parsed expression, available after the create method has been called.
Number of times the directive has been refreshed by a change in the reactive model