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 }}
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:
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 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. 🙂