Introduction
A program’s requirement may be to allow the user to key in some characters and, based on the keyed-in value, display the matching results. This is where we use typeaheads to display results based on the entered text in a text box. In this article, we will populate the typeahead dynamically from a Web service and the data displayed will be in a tabular format with headers. This is all implemented using Angular JS.
Description
This article assumes that a Web service already exists and that it returns the data in a JSON format. For this example, we will use an API that returns the country name and the capital based on the characters keyed in by the user.
HTML
Ideally, the HTML and the JS code should be in separate files; however, for this example, we are keeping the JS code in the same file as the HTML. To begin with, let’s add references required to implement the typeahead in the HTML.
<script src="//ajax.googleapis.com/ajax/libs/ angularjs/1.5.3/angular.js"></script> <script src="//ajax.googleapis.com/ajax/libs/ 1.5.3/angular-animate.js"></script> <script src="//angular-ui.github.io/bootstrap/ ui-bootstrap-tpls-1.3.1.js"></script> <link href="//netdna.bootstrapcdn.com/bootstrap/ 3.3.6/css/bootstrap.min.css" rel="stylesheet">
To implement typeahead, we use the angular directive “typeahead.” The syntax is as follows:
1. <input type="text" 2. ng-model="selected" 3. placeholder="Countries typeahead" 4. uib-typeahead="country as countryTypeAheadLabel(country) for country in getCountry($viewValue)" 5. typeahead-loading="loadingLocations" 6. typeahead-popup-template-url="customPopupTemplate.html" 7. typeahead-no-results="noResults" class="form-control">
Script 1
There are two main components in the preceding syntax:
- uib-typeahead at Line 4 and
- typeahead-popup-template-url at Line 6.
Uib-typeahead again has two parts:
- One is countryTypeAheadLabel(country) and
- The other is getCountry($viewValue).
Method getCountry($viewValue) retrieves the data from the Web API. The definition of the function is as shown next:
1. $scope.getCountry = function (val) { 2. var urlStr = "https://webapiResource/rest/v1/country/" + val; 3. return $http.get(urlStr).then(function (response) { 4. return response.data; 5. }); 6. };
Script 2
$viewValue is a default parameter provided in Angular that contains the text that the user keys in. The ‘getCountry’ function takes the keyed-in value as a parameter and calls the REST API. The response returned from the Web API is the function’s return value.
Because REST API is an asynchronous call, there are two return statements: one when the Web API is invoked—$http.get—and one within the success callback of the Web API method. This ensures that the data is passed on to the “typeahead-popup-template-url” for display.
Method countryTypeAheadLabel formats the data or the country selected. Because, in this example, country is an object, it cannot be directly bound. If multiple properties are to be displayed for the selected data, this function is used for formatting the data.
1. $scope.countryTypeAheadLabel = function(country) { 2. if (country == null || country == undefined) 3. return; 4. 5. if (country.name == '' || country.name == undefined) { 6. return ''; 7. } 8. 9. var label = country.name + " - " + country.capital; 10. return label; 11. }
Script 3
The countryTypeAheadLabel function verifies if any country is selected. If selected, it concatenates and returns the country name and the capital.
Next is the “typeahead-popup-template-url“. This is an ng-template that displays the result in a tabular format.
1. <script type="text/ng-template" id="customPopupTemplate.html"> 2. <div class="custom-popup-wrapper" 3. ng-style="{top: position().top+'px', left: position().left+'px'}" 4. style="display: block;" 5. ng-show="isOpen() && !moveInProgress" 6. aria-hidden="{{!isOpen()}}"> 7. <div class="customDropdown"> 8. <div class="row" style='margin:0;'> 9. <div class="dropdownHead" style='width:100%;'> 10. <div class="col-sm-3"> 11. Country name 12. </div> 13. <div class="col-sm-3"> 14. Capital 15. </div> 16. </div> 17. </div> 18. <div ng-repeat="match in matches"> 19. <div class="row dropdown-row" ng-mouseenter="selectActive($index)" ng- click="selectMatch($index)" style=''> 20. <div class="col-sm-3"> 21. {{match.model.name}} 22. </div> 23. <div class="col-sm-3"> 24. {{match.model.capital}} 25. </div> 26. </div> 27. </div> 28. </div> 29. </div> 30. </script>
Script 4
A template is defined in a script tag with type as ‘text/ng-template’. Lines 8-17 define the header columns for the tabular display data. Lines 18-27 display the data. The content is stored in an collection named ‘matches’ and the syntax to access the object properties is “match.model.<property name>”. In the preceding example, we are displaying the country name and the capital.
The output looks as shown in Figure 1 when the user is keying in the text.
Figure 1: The output from keying in the text
Once the user selects one of the countries, it is displayed as shown in Figure 2:
Figure 2: After a country has been selected
In this example, we are displaying only two columns: the country and the capital. It can be used to display multiple columns by modifying the template; styling it will bootstrap classes.
Summary
In this example, the data was retrieved asynchronously and bound to the typeahead. The Web API is written such that it can take the keyed-in value as an input parameter and return the data; in other words, the filtering for the data is done within the Web API. The data is then bound to the template to display the data in a tabular format.
The Web service is invoked for every letter keyed in. This can potentially be a costly operation if the Web API takes time to return the data. Optimization techniques such as server side caching can be considered to improve the performance.