left-icon

Aurelia Succinctly®
by Matthew Duffield

Previous
Chapter

of
A
A
A

CHAPTER 10

Custom Binding Behaviors

Custom Binding Behaviors


In Chapter 5, we were looking at the available binding behaviors packaged with Aurelia. In this chapter, we will look into what it takes to build our own custom binding behavior. As you recall, binding behaviors are the most similar to value converters. You use the ampersand (&) character to denote that we will be using a binding behavior. Binding behaviors give us a lot more access to the binding than simply converting values like the value converter does.

Let’s consider the following use case. You are tasked with creating a registration screen that allows the user to choose a username. The system requires that this username be unique, so you must ensure that you perform a lookup as the user types. We will use the debounce binding behavior to help ensure that our custom binding behavior does not fire too many times. Let’s take a look at how this can be accomplished:

Code Listing 105

const interceptMethods = ['updateTarget', 'updateSource', 'callSource'];

export class UniqueBindingBehavior {

 bind(binding, scope, uniqueFunc) {

 let i = interceptMethods.length;

 while (i--) {

  let method = interceptMethods[i];

  if (!binding[method]) {

  continue;

  }

  binding[`intercepted-${method}`] = binding[method];

  let update = binding[method].bind(binding);

  binding[method] = uniqueFunc.bind(binding, method, update);

 }

 }

 unbind(binding, scope) {

 let i = interceptMethods.length;

 while (i--) {

  let method = interceptMethods[i];

  if (!binding[method]) {

  continue;

  }

  binding[method] = binding[`intercepted-${method}`];

  binding[`intercepted-${method}`] = null;

 }

 }

}

This custom binding behavior will simply try to execute the uniqueFunc reference that it has whenever the binding receives an updateTarget, updateSource, or callSource notification.

The uniqueFunc must have the following signature: (method, update, value). Let’s look at how this is wired in markup:

Code Listing 106

<template>

 <require from='unique-binding-behavior'></require>

 <h1>${title}</h1>

 <form>

 <label>User Name</label>

 <input value.bind="username & unique:uniqueFunc & debounce:800">

 <p class="help-text">${lookupResult}</p>

 </form>

</template>

In this snippet, we actually have two binding behaviors. One is the debounce behavior that we get for free from Aurelia. We are stating that we don’t want to update the firstName property on the view-model until 800 milliseconds have passed since the user has pressed a key. We also are using our unique custom binding behavior. It has a single argument that is a reference to the uniqueFunc function on the view-model. You will also notice that we also have a paragraph element that displays the value of the lookupResult property.

Let’s now look at our view-model and see what we are doing here:

Code Listing 107

export class App {

 username = '';

 uniqueNames = ['matt', 'kevin', 'larry'];

 lookupResult = '';

 uniqueFunc;

 constructor() {

 this.uniqueFunc = (method, update, value) => {

  if (this.uniqueNames.includes(value)) {

  this.lookupResult = 'Sorry this username has already been used.';

  }

  else {

  this.lookupResult = '';

  }

  update(value);

 };

 }

}

As you can see from our view-model, we have a simple array, uniqueName, representing a list of names already in use. I use an array with fixed values for simplicity; in a real application, you’d have to update the array values as names are added. We have our lookupResult to allow us to display a message back to the user if the username that is typed has already been used. Finally, in our constructor, we have our uniqueFunc logic that simply checks to make sure the value typed is unique.

Here is a screenshot of the custom binding behavior in action:

UniqueBindingBehavior

Figure 30: UniqueBindingBehavior

You can look at this example here.

This is handy, but it still required a lot of code in our view-model to accomplish. What if we wanted to have the custom binding behavior handle the bulk of the logic? Let’s take a stab at reworking our binding behavior to do just that. Consider the following:

Code Listing 108

const interceptMethods = ['updateTarget', 'updateSource', 'callSource'];

export class UniqueBindingBehavior {

 uniqueFunc(method, update, $this, uniqueArray, resultHandler, value) {

 if (uniqueArray.includes(value)) {

  $this[resultHandler] = 'Sorry this username has already been used.';

 }

 else {

  $this[resultHandler] = '';

 }

 update(value);

 }

 bind(binding, scope, $this, uniqueArray, resultHandler) {

 let i = interceptMethods.length;

 while (i--) {

  let method = interceptMethods[i];

  if (!binding[method]) {

  continue;

  }

  binding[`intercepted-${method}`] = binding[method];

  let update = binding[method].bind(binding);

  binding[method] = this.uniqueFunc.bind(binding, method,

  update, $this, uniqueArray, resultHandler);

 }

 }

 unbind(binding, scope) {

 let i = interceptMethods.length;

 while (i--) {

  let method = interceptMethods[i];

  if (!binding[method]) {

  continue;

  }

  binding[method] = binding[`intercepted-${method}`];

  binding[`intercepted-${method}`] = null;

 }

 }

}

This time, we have placed the logic of determining the uniqueness of the username into the custom binding behavior. It is flexible enough to receive any array. It also has a generic resultHandler that allows you to update the view-model once uniqueness has been determined. Our custom binding behavior now is expecting three arguments: $this, uniqueArray, and resultHandler. We need a handle to the view-model, $this, so that we can use the string representation held in the resultHandler property to update the view-model. Finally, we also expect an array that will check the uniqueness of the value entered.

Here’s how the markup would look for this:

Code Listing 109

<template>

 <require from='unique-binding-behavior'></require>

 <h1>${title}</h1>

 <form>

 <label>User Name</label>

 <input value.bind="username & unique:$this:uniqueNames:'lookupResult' & debounce:800">

 <p class="help-text">${lookupResult}</p>

 </form>

</template>

Our custom binding behavior uses the $this keyword that we get for free from Aurelia to reference our view-model. Next, we are passing in a reference to the uniqueNames array. Finally, we pass a string representing the lookupResult property so that we can update the view-model from the custom binding behavior. The change that we need to make to our view-model is simply to remove the uniqueFunc function, as it is no longer necessary.

You can see this example in action here.

Please refer here for more information on custom binding behaviors.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.