Search code examples
androidcordovaangularjs-scopeionic-frameworkestimote

Update Ionic view from async callback


I am using the phonegap-estimotebeacons plugin in my Ionic app to range some beacons. When it gets updated beacon info, I want it to update the view, but my list doesn't get updated until I switch to a different tab and then switch back.

Here's my view that lists all the found beacons:

<ion-view view-title="Beacons">
  <ion-content>
    <ion-list>
      <ion-item ng-repeat="beacon in beacons" type="item-text-wrap">
        <span>{{beacon.macAddress}}</span>
        <span>{{beacon.distance}}</span>
      </ion-item>
    </ion-list>
  </ion-content>
</ion-view>

Here's the controller that renders the view:

.controller('BeaconsCtrl', function($scope, Beacons) {
  $scope.beacons = [];
  Beacons.range($scope);
})

The Beacons factory then starts to range the beacons and update the scope on every success callback:

.factory('Beacons', function() {
  rangeBeacons = function($scope) {
    var success = function(beaconInfo) {
      $scope.beacons = beaconInfo.beacons;
    };

    var failure = function(errorMessage) {
      alert(errorMessage);
    };

    EstimoteBeacons.startRangingBeaconsInRegion(
      {},
      success,
      failure
    );
  };

  return {
    range: rangeBeacons
  };
})

From other examples I've seen, it looks like my view should react when I update the $scope object, but I have to go to a different tab and then come back in order for my view to be updated. What can I do to get the view to update as soon as the $scope variable is modified?

Update

Modified the controller code to show that I have attempted to initialize $scope.beacons to an empty array, but this makes no difference.


Solution

  • I found the solution. Because the callback takes place outside of the digest cycle, we need to call $scope.$apply(). Initializing the beacons to an empty array is not necessary.

    Updated controller code:

    .controller('BeaconsCtrl', function($scope, Beacons) {
      Beacons.range($scope);
    })
    

    Updated service code:

    .factory('Beacons', function() {
      rangeBeacons = function($scope) {
        var success = function(beaconInfo) {
          $scope.beacons = beaconInfo.beacons;
          $scope.$apply();
        };
    
        var failure = function(errorMessage) {
          alert(errorMessage);
        };
    
        EstimoteBeacons.startRangingBeaconsInRegion(
          {},
          success,
          failure
        );
      };
    
      return {
        range: rangeBeacons
      };
    })