Web dev and more

Aurelia Components: Function Binding with .call

February 05, 2017 | 4 Minute Read

One principle of Aurelia is its usage of custom components based on the web component specifications. That means, that your application is based on custom components and attributes and you can leverage this to slice your code base into small, reusable and maintainable chunks. I want to write about some concepts to show the capabilities of Aurelia and how you can use them to write awesome custom components/attributes, which can help to avoid complex view models. This is the first post about function bindings, there will be further posts about other things.

.call binding command

You can use the .call binding command to pass functions references into your custom component. With this, you can achieve callbacks or kind of interceptors you can call before doing something inside the component. Therefore you can execute code from within your component that is located somewhere outside in the containing model.

The .call binding command can be bind to custom elements or used with custom attributes:

//custom element
<button-component my-binable-name.call="callback()">Say Hello</button>

//custom attribute
<div my-attribute.call="my-bindable-name.call:callback()"></my-button>

It is possible to write the custom attribute in a more readable fashion, at least for one primary bindable. May take a look here at the Aurelia Blog.

Examples

In the first example, I use the .call bind to pass a callback function on a custom checkbox element. We are passing our callback function and the labels for the checkbox.

usage in app.html

<checkbox-component callback.call="checkboxToggle()" labels.bind="{ true : 'checked', false: 'not checked'}"></checkbox-component>

As soon as the user clicks the checkbox, the change.delegate will call directly our callback function, that we passed to the element trough the .call binding. We could also set some function inside the element, do something and then call the passed function.

checkbox-component.html

<template>
  <input type="checkbox" checked.bind="checkValue" change.delegate="callback()"> ${labels[checkValue]}
</template>

checkbox-component.js

import { bindable } from 'aurelia-framework';

export class CheckboxComponentCustomElement {
  @bindable callback;
  @bindable labels;
  
  checkValue = false; //default state
}

This example could be used inside a kind of form controller which would use the callback to en- or disable parts in a form dependent on the checkbox state.


The next example is using a custom attribute for dynamic data loading. This custom attribute can be applied to any list, you just need to define the function that will care about the data loading.

usage in app.js

 <ul scroll-end="load.call:loadItems()" class="list">
    <li repeat.for="item of items">
      ${item}
    </li>
  </ul>

scroll-end-component.html

import { bindable , inject } from 'aurelia-framework';

@inject(Element)
export class ScrollEndCustomAttribute {
  
  @bindable load;
  element;
  
  constructor(element) {
    this.element = element;
  }
  
  attached() {
    this.element.addEventListener('scroll', () => {
        if (this.element.scrollTop + this.element.offsetHeight >= this.element.scrollHeight) {
            this.load();
        }  
    });
  }
}

For checking the scroll position within the list, we inject the element and start to listen for scroll events. As soon as we determine that the user scrolled until the end, we call our load function. This will extend the itemsArray to which our list is bound to.

loadItems() {
  //here you could do a REST call e.g.
  for(var i = 1; i <= 5; i++)
  this.items.push('new item ' + i);
}

.call is your friend

The .call binding is somehow underestimated, at least it is not much documented, and the most examples you can find about it are only at Stack Overflow. If you want to react in your view model on something that happens inside your component, this can be a much better solution then sending events around or setting up a shared service.

You can find the checkbox- and the scroll-end- component as well as a one more simple example on this GistRun.

Found some typo, want to give feedback or discuss? Feel free to contact me :)