0

My directive uses a service which returns a promise, I need to display the scope attributes geoZip, geoCity and geoState in the template.

The issue is that those scope variables are not being shown in the template, I just see the comma.

What should I do to make it display the scope variables?

This is my directive code:

  .directive('cityStateZip', function() {
      return {
        restrict: 'A',
        transclude: true,
        scope: {
          zip: '=',
        },
        template: '<p>{{geoCity}}, {{geoState}} {{geoZip}}</p>',
        controller: ['$scope', 'GeolocationService', function ($scope, GeolocationService) {
          GeolocationService.geocode($scope.zip).then(function(result) {
            if (result) {
              console.log(result);
              $scope.geoZip = result['address_components'][0]['long_name'];
              $scope.geoCity = result['address_components'][1]['long_name'];
              $scope.geoState = result['address_components'][2]['short_name'];
            }
          });
        }]
      };
    })

.service('GeolocationService', ['underscore', '$q', function (underscore, $q) {
  var gs = {};

  gs.geocode = function(address) {
    var geocoder = new google.maps.Geocoder();
    var deferred = $q.defer();
    geocoder.geocode( { "address": address }, function(results, status) {
      if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
        return deferred.resolve(underscore.first(results));
      }
      return deferred.reject();
    });
    return deferred.promise;
  }

  return gs;
}]);
victorhazbun
  • 786
  • 8
  • 16
  • 1
    Show the `geocode()` service function. Judging by property names it looks like you are using google...which is outside angular context and you will need $apply in there somewhere to tell angular to run a digest – charlietfl Jan 04 '16 at 01:52
  • yes, but how I should use the $apply method in my code? – victorhazbun Jan 04 '16 at 01:53
  • 1
    Can use it in the service function ...in google geocode callback or in the controller. Curious though what promise is being returned – charlietfl Jan 04 '16 at 01:54
  • when I use the $scope.apply() just after the line which assigns $scope.geoState I get an error: "$digest already in progress". https://docs.angularjs.org/error/$rootScope/inprog?p0=$digest – victorhazbun Jan 04 '16 at 01:57
  • show the service function – charlietfl Jan 04 '16 at 01:58
  • I just added the service in the SO question – victorhazbun Jan 04 '16 at 02:04
  • Ok, what does the `console.log(result)` produce? – Phil Jan 04 '16 at 02:05
  • Object {address_components: Array[4], formatted_address: "Provo, UT 84604, USA", geometry: Object, place_id: "ChIJ8zmgt8mRTYcRMiwHiJ7_Nw8", postcode_localities: Array[2]…} address_components: Array[4] formatted_address: "Provo, UT 84604, USA" geometry: Object place_id: "ChIJ8zmgt8mRTYcRMiwHiJ7_Nw8" postcode_localities: Array[2] types: Array[1] __proto__: Object – victorhazbun Jan 04 '16 at 02:06
  • Not seeing `long_name` or `short_name` in there. Can you confirm that those properties exist? – Phil Jan 04 '16 at 02:08
  • @Phil nailed it, when you send sketchy address like just a zipcode you don't get much back – charlietfl Jan 04 '16 at 02:09
  • Object {address_components: Array[4], formatted_address: "Provo, UT 84604, USA", geometry: Object, place_id: "ChIJ8zmgt8mRTYcRMiwHiJ7_Nw8", postcode_localities: Array[2]…} address_components: Array[4] 0: Object long_name: "84604" short_name: "84604" types: Array[1] __proto__: Object 1: Object long_name: "Provo" short_name: "Provo" types: Array[2] __proto__: Object – victorhazbun Jan 04 '16 at 02:09
  • 1
    put code in question where it can be formatted and read...not dumped into comment blocks – charlietfl Jan 04 '16 at 02:10
  • I used the $timeout service to wrap the variable assignation and worked, but I don't think thats the best solution. – victorhazbun Jan 04 '16 at 02:10
  • actually using `$timeout` is another way to call `$apply` without running into digest problems. Not sure why you are running into that issue though since $q should have worked without needing $apply – charlietfl Jan 04 '16 at 02:12
  • is there another alternative of $timeout ? – victorhazbun Jan 04 '16 at 02:17
  • What version of angular are you using? – Anthony Patton Jan 04 '16 at 03:19

1 Answers1

1

I found that I have to use the $timeout service to make it work:

.directive('cityStateZip', function() {
  return {
    restrict: 'A',
    transclude: true,
    scope: {
      zip: '=',
    },
    template: '<p>{{geoCity}}, {{geoState}} {{geoZip}}</p>',
    controller: ['$scope', '$timeout', 'GeolocationService', function ($scope, $timeout, GeolocationService) {
      GeolocationService.geocode($scope.zip).then(function(result) {
        if (result) {
          console.log(result);
          $timeout(function() {
            $scope.geoZip = result['address_components'][0]['long_name'];
            $scope.geoCity = result['address_components'][1]['long_name'];
            $scope.geoState = result['address_components'][2]['short_name'];
          });
        }
      });
    }]
  };
})

Please let me know if there is best alternative (not using $timeout), Thanks!

victorhazbun
  • 786
  • 8
  • 16
  • 1
    `$timeout` is recommended by the AngularJS authors as the best way to do a "safe $apply". The GeolocationService is asynchronous to the AngularJS digest cycle. `$timeout` gives the browser a chance to catch up and sync back with the digest cycle. – georgeawg Jan 04 '16 at 04:04