From Angular to Aurelia Part 3: Value Converters

This is the third part of the From Angular to Aurelia Series. In the previous post we looked at how Services are used in Angular and their counterpart in Aurelia. This post focuses on Value Converters (VC), which are known as Filters in the world of Angular.

Transforming binding outputs

The sole reason of Value Converters is to transform data-streams from bindings, either from the Model to the View or the other way around. That way you can decoratively transform your data step-wise and adapt it to the requirements of your View, without having to modify the underlying original data-source itself. Filters and VCs might be used on every binding but do get used most often in combination with repeaters, where you want to transform the representation of a list of items. Imagine features like sorting, filtered views and so on.

Filters in Angular

The syntax to use filters in Angular looks like this:

<ul>  
  <li ng-repeat="transaction in transactions">{{item | currency:"€"}} </li>
</ul>  

You place a pipe | after your expression, which forwards the value as the first argument to the filter. Additional arguments may be provided by prefixing them with a colon. The above is a predefined currency-filter responsible for displaying a given number along with a currency symbol.

In order to register a custom filter the app-module object offers the method filter. This takes the name of the filter as first parameter, followed by a factory function returning the filters body. This inner function contains at least one argument, being the provided value to transform. In the functions body you do your magic and return the transformed data.

app.filter('myFilter', function() {  
  return function(value) {
    return value + " transformed!!!";
  }
});

The respective call in your markup would be:

<p>{{'My data' | myFilter }}</p>  
// results in
<p>My data transformed!!!</p>  

Parsers and Formatters

That way we covered the way from the VM, respectively Controller, to the View. That works great for normal bindings. But if we need to work with form elements, which act two way you have to leverage a different mechanism called $parsers/$formatters. An example for this might be an input field where we'd like the user to input his name and automatically prefix it with a GitHub like @ sign, but actually store the clean name.

In order to do so Angular requires you to create a custom directive. In there you declare the dependency on the ngModel, which is a Service used with form elements. In the link phase you need to get hold of the Controller to attach the mentioned $formatters — used for the view display — and the $parser — used for parsing data back to the controller.

app.directive('github', function() {  
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      //format text going to user (model to view)
      ctrl.$formatters.push(function(value) {
        return "@" + value;
      });

      //format text from the user (view to model)
      ctrl.$parsers.push(function(value) {
        return value.replace("@", '');
      });
    }
  };
});

The markup would look like this, where the p tag below the input element shows you that the parsing properly did its job:

<body ng-controller="MainCtrl">  
  <form name="form" class="css-form" novalidate>
    <input type="text" ng-model="inputValue" github />
    <p>{{ inputValue }}</p>
  </form>
</body>  

You can see the full example in this plunker.

The Aurelia Way

So far I hope you've already realized that most of the things done in Aurelia follow a certain convention and always focus on the developer experience. A ValueConverter, which's naming actually feels more consistent compared to filter, is used in the same way like Angulars Filters:

<p>${'My data' | myFilter }</p>  
// results in
<p>My data transformed!!!</p>  

In order to create a custom ValueConverter you create and export a new class, following Aurelias convention of [Name]ValueConverter:

//File: my-filter.js
export class MyFilterValueConverter {  
  toView(value) {
    return value + " transformed!!!";
  }
}

You'd have to either import the VC locally in your View or register it globally. First is done by adding this somewhere in your Views markup:

<require from="./my-filter"></require>  

the latter by registering the ValueConverter via the aurelia object during custom bootstrapping:

export function configure(aurelia) {  
  console.log(aurelia);
  aurelia.use
    .standardConfiguration()
    .developmentLogging();

  aurelia.globalizeResources('./my-filter');

  aurelia.start().then(a => a.setRoot('app', document.body));
}

As for the way back with input elements, I have good news. Stick with one concept instead of learning new things. The GitHub Filter would look like this as a Aurelia VC:

// File: github.js
export class GithubValueConverter {  
  toView(value) {
    return "@" + value;
  }

  fromView(value) {
    return value.replace("@", '');
  }
}

As you see per convention you just implement another hook called fromView which intercepts the write-backs from the input to the property. In your View markup you now may keep using the VC even on input elements:

<input type="text" value.bind="inputValue | github"/>  
<p>${ inputValue }</p>  

Conclusion

I hope to have been able to highlight one of Aurelias key concepts, which is not getting in your way and letting you focus on productivity. This is achieved by not introducing several concepts for similar tasks.
There are more features we haven't looked at here. If you're interested I highly recommend the official blog post by core team member Jeremy Danyow, which will walk you through additional features like chained VCs, object parameters and a bunch of cool examples.

Photo credit: The Forgotten Memories Theatre via photopin (license)