We have a form or at least an input field and we want to submit or execute some behaviour when the user presses Enter. In this tutorial I'm going to show you how to build an Aurelia custom attribute that can be used wherever you want this feature.
Our goal is to use it like this
<input type="text" on-enter.call="submit()">
Let's create the attribute
import {customAttribute, autoinject} from "aurelia-framework";
@autoinject
@customAttribute('on-enter')
export class OnEnter {
constructor(element:Element) {
this.element = element;
this.onEnter=(ev:KeyboardEvent) => {
//Enter keyCode is 13
if (ev.keyCode !== 13) return;
this.action();
};
}
element:Element;
attached() {
this.element.addEventListener("keyup",this.onEnter);
}
onEnter;
action;
valueChanged(func) {
this.action = func;
}
detached() {
this.element.removeEventListener("keyup",this.onEnter);
}
}
Yes, that's all, but now let me explain it. The autoinject
decorator tells Aurelia to use metadata generated by TypeScript to determine the dependency type (in this case a DOM Element. This is normal usage in Aurelia, we do that everyhwhere we need to inject dependencies (and we're using TypeScript). The customAttribute
is self-explaining.
In the constructor we need the html element where the attribute is used and that's what it gets injected here. Next, we set up the 'onEnter' function. This part is quite important so pay attention.
We need that function as an event handler for a keyup
event. You see that we have the attached()
and detached()
methods which are part of the Aurelia custom element lifecycle i.e they are invoked automatically when the view is attached/detached from the DOM. In this case we don't have a view, but the methods are still invoked by the framework (I suppose when the parent element is attached/detached) allowing us to set up or remove the event handler. That's why we have the onEnter
variable instead of using arrow functions (lambdas).
Now, you may wonder why onEnter
isn't defined as a function directly. It's because of javascript and the this
affair. In a simple function this
would be the calling context i.e the window. But we need this
to point at our attribute model and using lambdas is the easiest way to do it (TypeScript takes care of the details).
The valueChanged
method is automatically invoked by Aurelia when the value of the attribute changes, and in this case we get the function that should be invoked on enter. Note how the binding syntax in html is not .bind
but .call
. This tells Aurelia to create and pass a delegate (which will wrap the specified function) as a value. So, when we invoke this.action()
we're actually invoking the delegate which in turn invokes the specified function.
And pretty much that's it. Once you're aware of the this
gotcha and the .call
binding you can create all sorts of 'event handler as an attribute'. For example, it's trivial to have an on-escape
attribute (hint: replace 13 with 27).
Custom attributes still need to be referenced in a view so you either need a <require from="myAtttribute"></require>
or set it up as a global resource or a feature. You can read more about those in Aurelia docs