How to create a country-picker Aurelia component

Sean Hunter
CloudBoost
Published in
8 min readOct 1, 2017

--

Common advice I’ve come across when building apps using SPA frameworks is to avoid using jQuery and other direct DOM manipulation techniques in favor of letting the framework update the DOM as a result in changes in your models state. This is generally fantastic advice, and I try to follow it where possible in my own Aurelia projects. But, often situations arise where a third party library (even ghasp a jQuery plugin) already exists that perfectly fits the problem at hand. One of the problems that plugins like selectize and select2 solve well is enhancing the out of the box functionality of the select element to provide a better user experience with auto-complete and more. In this post, we’ll look at how you can create a country picker component by combining the jQuery based selectize control, along with a country flags library to create a country picker component in Aurelia.

This article will cover the following points:

  • Adding the selectize control into an Aurelia CLI project using NPm and requireJS
  • Plugging the selectize control into the Aurelia component life-cycle using the attached callback
  • Dynamically compiling Aurelia templates on the fly to render country select items

By the end of this post, we’ll have created a working country-picker control that allows users to pick their country from an auto-complete list. To make this list a little more user-friendly we’ll render the corresponding country flag associated with a given country code. You can see the end result in the below screengrab:

You can see the custom element in action here.

Setting up the pre-requisites

note: This assumes you’ve already created an Aurelia project using the requireJS module loader. For information on getting started with the Aurelia CLI, check out this article on the Aurelia Hub.

To begin with we’ll need to install the three per-requisite packages via NPM: selectize, and flag-icon-css, and of course jquery:

With these installed, the next step is to modify the aurelia.json file as follows to ensure that the packages are bundled correctly into your Aurelia vendor-bundle.js file. In case you’ve not come across this yet, Aurelia CLI + requireJS projects create two bundles by default:

  • The app-bundle.js file which includes the view-models, view-templates and so on that make up your Aurelia project
  • The vendor-bundle.js file which includes the third party dependencies that your application relies on

You’ll notice that we’re modifying the dependencies section to include the new plugins. The selectize and flag-icon-css nodes include references to the css resources that should be bundled. This allows us to require them into our Aurelia view templates just like any other kind of view-resource, as you’ll see in a moment. For brevity, I’ve only included an excerpt with the relevant part of the file here. If you’re interested you can see the complete file here.

aurelia.json

With the bundling configured, most of our dependencies are now in place, but one more step remains. The flag icon files (used to render the country flag next to each country name) are referenced from the flag-icon.css file using relative paths like this url(../flags/4x3/ad.svg);, so we need to copy these files into that relative path from the root of the Aurelia application to have them load correctly. Fortunately, we can do this by modifying the build pipeline generated for us by the CLI. To do this we’ll create a new gulp task as shown below, and call it as a part of the build step. This file relies on input and output paths defined in the aurelia.json file above:

copy-flags.js

With this file in place, it can be included in the Aurelia build by modifying the build.js file as shown below:

build.js

With the dependencies in place, we can now get to the more fun part of creating the component. We’ll structure this by first creating a generic selectize component that takes an http-query used to pull a list of results back as a JSON blob, and render the results as select items using an Aurelia view template.

Creating the ux-selectize component

We’ll create the ux-selectize component as a custom attribute. One benefit of doing it this way rather than using a custom element is that the select element can remain pretty-well unchanged. By applying the custom attribute to the select element we can transform it into a selectize control, in a way that’s quite unobtrusive from perspective of the view template. Also, due to the way that selectize works, we’d need to know the custom element options up-front (passing them down from a parent view-model or using a static list), rather than fetching them dynamically as we are in this case, which rules out the option of using a custom element in this scenario.

There are a few interesting parts to note about this custom attribute. Firstly, we’re using the attached Aurelia component life-cycle call-back to initialize the jQuery component. This is the preferred life-cycle hook to perform DOM interaction as it occurs when a component is attached to the DOM. It’s also worth noting that we’re using unbind component life-cycle method to clean up the selectize control when the view is unbound.

The dynamicOptions decorator is used to add new bindable properties to the custom attribute at will without the need to declare them one-by-one. For performance reasons you may want to declare them individually if you have a well-known and controlled set of properties, you intend to bind against. Looking down further, we’ve Aureliarized the HTTP logic as well. Instead of using jQuery AJAX to fetch the country list from the remote server, we’re instead passing a function, which makes the call using the aurelia-fetch-client module (as you’ll see shortly). Finally, we’re hooking into the onChange selectize event and delegating it to the select element. This will allow us to listen for this event from the country-picker parent component and use the updated country value.

I’ll ask you to ignore the magic in the getHtml method at the bottom of the file for now, and we’ll circle back to that later.

ux-selectize.js

With this component in place, the next step is to create a country-picker custom element to encapsulate the selectize component, and provide the country-picking specific implementation details. This component imports the flag-icon, and selectize CSS files, and declares a new select element decorated with the ux-selectize custom attribute. The selectize attribute binding expression takes 6 parameters:

  • httpQuery : The method (declared on the country-picker view-model used to fetch the countries from the server)
  • selected : The currently selected country
  • templateUrl : The path to the country item template (relative path within the project)
  • valueField : The field on the country model to use as the select option value
  • searchField : The field on the country model to use as the lookup key
  • labelField : The field on the country model used for the select option label

We also specify an initial option to be used when the select component is initialized. This includes the selected country name and code:

<option value.bind="selected.code">${selected.name}</option>

Creating the country-picker component

country-picker.html (view)

The next step is to create the corresponding country-picker view-model. This defines the getCountries method used to fetch countries from the countries.json endpoint using the aurelia-fetch-client module. This list is filtered by the country name that the user types into the input element. To improve efficiency filtering logic would be better implemented on the server side to minimize the payload we need to pull back from the server.

We’re also listening for the countryChange event fired from the ux-selectize view-model in order to set our currently selected country.

country-picker.js (view-model)

..and the magic

I promised that we’d circle back to how the ux-selectize component renders its item templates, and now is the time. The selectize control has a render method which you can pass HTML that should be rendered for each select item. A simple implementation would have just been to return a string literal representing the HTML we wished to return, which would have looked something like this:

Even though this approach works fine, I generally prefer to keep my HTML templates as pure HTML, separated from the JavaScript files. I wanted to be able to create an item template at /templates/country-item.html, then have Aurelia’s binding engine take care of data-binding the item and creating the output HTML. It turns out that this is doable, with the help of Aurelia’s ViewEngine class.

First, let’s take a look at the template that we’ll use to render each of the country select items. This is a basic Aurelia view template which uses string interpolation binding to render country details to the view. I’ll not go into this in too much detail:

country-template.html

So how is this template used by the ux-selectize custom attribute? To begin with, we use the viewEngine to create a view factory given the template URL this.viewEngine.loadViewFactory(templateUrl). We do this up-front to avoid needing to repeat the process for every select item. Once we’ve got the view factory, we can use it to create a view instance const view = factory.create(childContainer); and bind the current select item using the view.bind method: view.bind(item);. To get the actual HTML of the view instance we need to attach it to a DOM element, which in our case is a wrapper div: let country = DOM.createElement("div"), view.appendNodesTo(country);. Finally, we can return the actual outerHTML of the element to be rendered.

I’ve glossed over the details of how the view engine process works under the hood here, since the main purpose is to give you the gist of what is happening. If you’re interested in reading more, I’d recommend this great article by Jeremy Gonzalez which I used as a reference when figuring this stuff out.

Put all of this together, and we have a re-usable country picker component, built on top of the jquery based selectize utility.

Nice! But, what have we learned?

It’s not advisable to go wholesale DOM manipulation and jQuery in an Aurelia project, as you start to get away from the benefits that Aurelia’s powerful templating and binding system gives you. But, sometimes there is an existing tool that solves your problem perfectly. In cases where this tool exists as an NPM package (and let’s be honest what doesn’t these days), it’s a simple matter of installing it into your Aurelia project, and making the relevant configuration tweaks to your aurelia.json file.

We also saw how you can use the dynamicOptions decorator to add bindable properties to your custom attributes at will, without the need to declare each property individually.

Finally, we saw that although Aurelia’s binding engine API can be daunting at first, it’s powerful when you get to understand a few of the building blocks. An example of one of these building blocks is the viewEngine.loadViewFactory method, used for creating view instances given a template URL. This gives you the flexibility of creating your own patterns when Aurelia’s default templating and binding approaches don’t quite fit what you need to do.

If you’re interested in seeing more implementation detail, the source code for the project is available on GitHub.

Originally published at sean-hunter.io on October 1, 2017.

--

--

Software Developer, rock climber and general geek ;). Author of Aurelia in Action.