AngularJS Country Picker

Every time that I need to implement a registration form there is some source of country selector; thus I decided to create a small component to generate the list of countries as options of the select element.

The ISO 3166 defines three different codes (two-letter, three-letter and numeric) for every country so I wanted this to be configurable in the component, bounding the ngModel to any desired value.

And of course, I didn’t want to wrap or rewrite the native select, but only work with the ngOptions attribute.

Implementation

Trying to implement this on a clean way was a really interesting exercise which made me struggle for a while but also learn more about the internals of AngularJS.

First I learnt about the execution order of the compile, preLink and postLink functions when using an element directive with an attribute directive. And you can see the results opening the console of this JSFiddle.

After that lesson I was sure that I needed to use the compile function of my custom directive to set the ngOptions of the element, because it gets fired first. But I couldn’t solve it until I realized that the tAttrs passed as an argument of the compile function is shared between all directive compile functions.

Thanks to that discovery the final implementation was easier than expected since we just need to set the ngOptions on the shared tAttrs list during the compile phase. As a side note I want to clarify that the attributes object could have been modified either in the directive controller or the preLink function with similar outcome, but not in the postLink as its fired after the parent is linked.

angular.module('angular-country-picker',[])
  .directive('pvpCountryPicker', function() {
    var countries = [
      {"name":"Afghanistan","alpha2":"AF","alpha3":"AFG","numeric":"004"},
      // Alphabetical list of countries.
      {"name":"Zimbabwe","alpha2":"ZW","alpha3":"ZWE","numeric":"716"}];

    return {
      controller: function($scope) {
        $scope.countries = countries;
      },
      compile: function (tElement, tAttrs) {
        if(! tAttrs.pvpCountryPicker) {
          tAttrs.pvpCountryPicker = 'alpha2';
        }
        var ngOptions = 'country.' + tAttrs.pvpCountryPicker + ' as country.name for country in countries';
        tAttrs.$set('ngOptions',  ngOptions);
      },
      restrict: 'A'
    };
  });

To-Do

Most of the time in our community what yesterday worked today is outdated, and with the major rework being done in the beta version of Angular 1.4 my implementation does not work any-more. So hopefully I will be able to fix it before Angular 1.4 become stable. I fixed it with a different approach.

Additionally I read some old post about redesigning the country selector which makes some interesting points hence I would like to work that idea in the future.

But for now you can install it using Bower or fork it from the repository.

$ bower install angular-country-picker
Advertisements

4 thoughts on “AngularJS Country Picker

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s