1

I updated my array from service but view wasn't updated.

And when I call $scope.$apply() or $scope.$digest, it logs

$digest already in progress

This is my main.html file. ctrl.flightsTo is the array I mentioned

<md-progress-linear class="md-accent" ng-hide="ctrl.isFlightDataLoaded" mode="indeterminate"></md-progress-linear>
<div class="md-padding">
  <md-autocomplete
      ng-show="ctrl.isFlightDataLoaded"
      md-selected-item="ctrl.selectedItem"
      md-selected-item-change="ctrl.selectedItemChange(item)"
      md-search-text="ctrl.searchText"
      md-items="item in ctrl.querySearch(searchText)"
      md-item-text="item.name"
      md-min-length="0"
      placeholder="Choose a destination">
    <md-item-template>
      <span md-highlight-text="ctrl.searchText">{{item.name}} - {{item.iata}}</span>
    </md-item-template>
  </md-autocomplete>
  <ul>
    <li ng-repeat="flight in ctrl.flightsTo">{{flight}}</li>
  </ul>
</div>

And this is my MainCtrl.

When md-selected-item-change event is called, I get my flight data from flightDataService in selectedItemChange function.

But Angular doesn't update views.

.controller('MainCtrl', function(flightDataService) {
    var self = this;
    self.isFlightDataLoaded = false;
    self.destinations = [];
    self.querySearch = querySearch;
    self.flightsTo = [];
    self.selectedItemChange = selectedItemChange;
    self.init = init;

    function querySearch(query) {
      var results = query ? self.destinations.filter(createFilterFor(query)) : self.destinations;
      return results;
    };

    function createFilterFor(query) {
      var lowercaseQuery = angular.lowercase(query);

      return function filterFn(destination) {
        return angular.lowercase(destination.iata).indexOf(lowercaseQuery) === 0;
      }
    };

    function selectedItemChange(item) {
      if (typeof item !== 'undefined') {
        self.flightsTo = flightDataService.getFlightsTo(item.iata);
      }
    };

    function init() {
      flightDataService.loadAll().then(function() {
        self.destinations = flightDataService.getAirports();
        self.isFlightDataLoaded = true;
      }).catch(function(error) {
        console.log(error);
      });
    };

    self.init();
  });

Lastly, I attached my flightDataService.

It just returns saved data.

.factory('flightDataService', function($http, $q, papa) {
    var FLIGHT_DATA_URL = 'https://s3-ap-southeast-2.amazonaws.com/glow-dev-assets/flight-data-gz.csv';

    var self = this;
    self.flights = [];
    self.airports = [];
    self.routes = [];
    self.loadAll = loadAll;
    self.getAirports = getAirports;
    self.getFlightsTo = getFlightsTo;

    function loadAll() {
      var deferred = $q.defer();

      loadAirportData().then(function(airports) {
        loadFlightData().then(function(flights) {
          processFlightData(flights, airports);
          deferred.resolve();
        }).catch(function(error) {
          deferred.reject(error);
        });
      }).catch(function(error) {
        deferred.reject(error);
      });
      return deferred.promise;
    };

    function loadAirportData() {
      var deferred = $q.defer();
      $http({
        method: 'GET',
        url: '/assets/airports.json'
      }).then(function(response) {
        deferred.resolve(response.data);
      }, function(error) {
        deferred.reject(error);
      });
      return deferred.promise;
    };

    function loadFlightData() {
      var deferred = $q.defer();
      $http({
        method: 'GET',
        url: FLIGHT_DATA_URL
      }).then(function(response) {
        deferred.resolve(response.data);
      }, function(error) {
        deferred.reject(error);
      });
      return deferred.promise;
    };

    function processFlightData(data, airports) {
      self.flights = papa.parse(data, { header: true }).data;

      var dests = self.flights.map(function(flight) { return flight.destination });
      var destSet = new Set(dests);
      destSet.forEach(function(dest) {
        var airport = airports.find(function(airport) {
          return airport.iata === dest
        });

        if (typeof airport !== 'undefined') {
          self.airports.push(airport);
        }
      });

      self.flights.forEach(function(flight) {
        var origin = flight.origin;
        var dest = flight.destination;

        if (typeof self.routes[dest] === 'undefined') {
          self.routes[dest] = [];
        }
        if (typeof self.routes[dest][origin] === 'undefined') {
          self.routes[dest][origin] = {
            date: flight.date,
            delay: flight.delay,
            distance: flight.distance
          };
        }
      });

      return self.flights;
    };

    function getAirports() {
      return self.airports;
    };

    function getFlightsTo(destination) {
      return self.routes[destination];
    };

    return self;
  });

When I log ctrl.flightsTo on console.

It's always changed well.

But view stays same state as an empty array.

5
  • use $scope instead of var self = this; Commented Dec 16, 2016 at 9:21
  • @azad I already use $scope instead self. But it doesn't work. Commented Dec 16, 2016 at 9:30
  • did you call $scope.apply() directly after self.flightsTo = flightDataService.getFlightsTo(item.iata); this line? Commented Dec 16, 2016 at 10:44
  • I see you are using an http call, this answer here might help you stackoverflow.com/a/15476728/5272567 might need to do a $scope.apply() directly after the http calls Commented Dec 16, 2016 at 10:47
  • @Matthias I did call $scope.$apply() after self.flightsTo = flightDataService.getFlightsTo(item.iata); But it makes errors like $digest already in progress Commented Dec 17, 2016 at 4:33

4 Answers 4

1

Finally, I found!

Because my flightsTo variable was associative array, Angular doesn't update it.

So I changed it to normal array.

Also, the opinion of IAmDranged was also right.

Thank you everybody!

Sign up to request clarification or add additional context in comments.

Comments

0

This isn't so much an answer as a trick I sometimes use when I find myself in a similar situation: inject a $timeout and use it to make the final update outside the context of the $http call:

$timeout(function () {
    processFlightData(flights, airports);
    deferred.resolve();
}

Obviously this doesn't solve the problem or explain why it's happening, but it may be a practical workaround if you simply need to get your app working.

Comments

0

Try this: When you use a simple javascript function angular does not apply the changes to variable inside this function.

function selectedItemChange(item) {
  if (typeof item !== 'undefined') {
    $scope.flightsTo = flightDataService.getFlightsTo(item.iata);
    $scope.$$phase || $scope.$apply();
  }
};

Or you can also use function expressions by put your function in $scope varianles like

 $scope.functionName = function () {}

Comments

0

I initially deleted this comment, since I haven't done angular in a while and got confused with the controlleras syntax, but it actually still might be usuefull in some ways.

ngRepeat uses $watchCollection to detect changes in the array - which will track changes to individual values in the array. This is what the Angular documentation says.

Trying to look a bit further, it actually sounds like the $watchCollection may work on a copy of the array - which would actually just be a copy of the reference to the array - as first initiliazed by the application.

In your case, your are assigning to your array - so you change the reference it points to - every time you update it. But $watchCollection is still only monitoring values changes in the original array to which it has kept a reference to.

You may want to set up an extra watch to look out for reference changes to your array.

This is rather a comment than an actual answer but this was too long to go in the comment section. Hope this is helpfull.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.