Filtering Input Fields in AngularJS

What did I learn today?

Today, I came across a situation where I needed to have an input field containing a phone number format the number on page load and during user entry.  Formatting the phone number while the user was typing was easy, all it took was a pattern attribute and a simple directive.  But when I tried to use a filter to format the phone number on page load, it was more difficult than I expected.  The normal filter syntax was not usable and caused the screen to error.

Because I spent an hour or two looking for the best solution, I decided to write this blog post to remind me how to do this six month from now when I need to do this again.

Background Information

Directives

For more information about Directives – refer to the documentation.

If you have any experience with Angular ( 4 or whatever they are calling it now) or React, you’ve probably heard of components. I view components as modular blocks of code that make up a single feature.  This modularization makes components reusable and testable.  Why do I mention Angular Components in a post about AngularJS Directives?

Directives are a proto-component in that they make modular reusable code in an AngularJS app.  One difference though, is that they are often used to for view components and should not really have business rules associated with them.  In AngularJS, it is considered bad practice to put DOM manipulation in the controller so directives are the way to accomplish that task.

Filters

For more information about Filters- refer to the documentation.

Filters are just a way to format the value of a expression to the required format for the view.  There are quite a few built in ones but I usually find I write others.  Here are a list of built in features

  • Uppercase / Lowercase
  • Number
  • Currencies
  • Date/Time
  • JSON
  • LimitTo

The Issue

In a normal scenario when you want to use a filter it’s easy; you just use following syntax (the bolded text is how you do it):

PHONE NUMBER     {{ "5555555555" | tel }}

Phone Example Non-Input

And the results look great!  So recently I needed to format a phone number in an input field when a page is loaded (due to weirdly formatted phone numbers that were entered into a database) and when the user finished typing in an input field.  When I tried to do this on a input field I received the following error:

Phone Example with Input.png

Bummer!

The Solution

After doing lots of searching on how to apply a filter to an input field on page load, I finally came across a question on Stack Overflow with an answer to my question. Stack Overflow Question The answer here is for currency filters BUT it will work for phone numbers as well. The Directive

.directive("format", ["$filter", function ($filter) {
    return {
        require: "?ngModel",
            link: function (scope, elem, attrs, ctrl) {
                if (!ctrl) return;
                ctrl.$formatters.unshift(function (a) {
                    return $filter(attrs.format)(ctrl.$modelValue);
                });
                elem.bind("blur", function (event) {
                    var plainNumber = elem.val().replace(/[^\d|\-+|\.+]/g, '');
                    elem.val($filter(attrs.format)(plainNumber));
                });
            }
        };



The Filter

.filter('tel', function () {
    var phoneFormat = /^(\d{3})?(\d{3})(\d{4})$/;
    return function (input) {
        var parsed = phoneFormat.exec(input);
        return (!parsed) ? input : ((parsed[1]) ? '(' + parsed[1] + ') ' : '') + parsed[2] + '-' + parsed[3];
    }
});
The magic here is happening in the directive. The directive is basically taking in a formatting filter and applying it on page load.  Between on load filter and another directive that is formatting the phone number as users type, the phone number on this page will have perfectly formatted phone numbers all the time.

The Conclusion

All in all, this solution is rather simple and elegant just the way I like it.  To my future self, your welcome for writing this reminder. 🙂

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s