Project

General

Profile

1
'use strict';
2

    
3
/**
4
 * A directive for adding google places autocomplete to a text box
5
 * google places autocomplete info: https://developers.google.com/maps/documentation/javascript/places
6
 *
7
 * Usage:
8
 *
9
 * <input type="text"  ng-autocomplete ng-model="autocomplete" options="options" details="details/>
10
 *
11
 * + ng-model - autocomplete textbox value
12
 *
13
 * + details - more detailed autocomplete result, includes address parts, latlng, etc. (Optional)
14
 *
15
 * + options - configuration for the autocomplete (Optional)
16
 *
17
 *       + types: type,        String, values can be 'geocode', 'establishment', '(regions)', or '(cities)'
18
 *       + bounds: bounds,     Google maps LatLngBounds Object, biases results to bounds, but may return results outside these bounds
19
 *       + country: country    String, ISO 3166-1 Alpha-2 compatible country code. examples; 'ca', 'us', 'gb'
20
 *       + watchEnter:         Boolean, true; on Enter select top autocomplete result. false(default); enter ends autocomplete
21
 *
22
 * example:
23
 *
24
 *    options = {
25
 *        types: '(cities)',
26
 *        country: 'ca'
27
 *    }
28
**/
29

    
30
angular.module( "ngAutocomplete", [])
31
  .directive('ngAutocomplete', function() {
32
    return {
33
      require: 'ngModel',
34
      scope: {
35
        ngModel: '=',
36
        options: '=?',
37
        details: '=?'
38
      },
39

    
40
      link: function(scope, element, attrs, controller) {
41

    
42
        //options for autocomplete
43
        var opts
44
        var watchEnter = false
45
        //convert options provided to opts
46
        var initOpts = function() {
47

    
48
          opts = {}
49
          if (scope.options) {
50

    
51
            if (scope.options.watchEnter !== true) {
52
              watchEnter = false
53
            } else {
54
              watchEnter = true
55
            }
56

    
57
            if (scope.options.types) {
58
              opts.types = []
59
              opts.types.push(scope.options.types)
60
              scope.gPlace.setTypes(opts.types)
61
            } else {
62
              scope.gPlace.setTypes([])
63
            }
64

    
65
            if (scope.options.bounds) {
66
              opts.bounds = scope.options.bounds
67
              scope.gPlace.setBounds(opts.bounds)
68
            } else {
69
              scope.gPlace.setBounds(null)
70
            }
71

    
72
            if (scope.options.country) {
73
              opts.componentRestrictions = {
74
                country: scope.options.country
75
              }
76
              scope.gPlace.setComponentRestrictions(opts.componentRestrictions)
77
            } else {
78
              scope.gPlace.setComponentRestrictions(null)
79
            }
80
          }
81
        }
82

    
83
        if (scope.gPlace == undefined) {
84
          scope.gPlace = new google.maps.places.Autocomplete(element[0], {});
85
        }
86
        google.maps.event.addListener(scope.gPlace, 'place_changed', function() {
87
          var result = scope.gPlace.getPlace();
88
          if (result !== undefined) {
89
            if (result.address_components !== undefined) {
90

    
91
              scope.$apply(function() {
92

    
93
                scope.details = result;
94

    
95
                controller.$setViewValue(element.val());
96
              });
97
            }
98
            else {
99
              if (watchEnter) {
100
                getPlace(result)
101
              }
102
            }
103
          }
104
        })
105

    
106
        //function to get retrieve the autocompletes first result using the AutocompleteService 
107
        var getPlace = function(result) {
108
          var autocompleteService = new google.maps.places.AutocompleteService();
109
          if (result.name.length > 0){
110
            autocompleteService.getPlacePredictions(
111
              {
112
                input: result.name,
113
                offset: result.name.length
114
              },
115
              function listentoresult(list, status) {
116
                if(list == null || list.length == 0) {
117

    
118
                  scope.$apply(function() {
119
                    scope.details = null;
120
                  });
121

    
122
                } else {
123
                  var placesService = new google.maps.places.PlacesService(element[0]);
124
                  placesService.getDetails(
125
                    {'reference': list[0].reference},
126
                    function detailsresult(detailsResult, placesServiceStatus) {
127

    
128
                      if (placesServiceStatus == google.maps.GeocoderStatus.OK) {
129
                        scope.$apply(function() {
130

    
131
                          controller.$setViewValue(detailsResult.formatted_address);
132
                          element.val(detailsResult.formatted_address);
133

    
134
                          scope.details = detailsResult;
135

    
136
                          //on focusout the value reverts, need to set it again.
137
                          var watchFocusOut = element.on('focusout', function(event) {
138
                            element.val(detailsResult.formatted_address);
139
                            element.unbind('focusout')
140
                          })
141

    
142
                        });
143
                      }
144
                    }
145
                  );
146
                }
147
              });
148
          }
149
        }
150

    
151
        controller.$render = function () {
152
          var location = controller.$viewValue;
153
          element.val(location);
154
        };
155

    
156
        //watch options provided to directive
157
        scope.watchOptions = function () {
158
          return scope.options
159
        };
160
        scope.$watch(scope.watchOptions, function () {
161
          initOpts()
162
        }, true);
163

    
164
      }
165
    };
166
  });
(17-17/18)