0

I have an app where my main state makes a resolve makes an http call and fetches an array.

Then, the child array is supposed to display one object from this array, but it seems that the variables from the controller are defined too early and don't get updated properly, so the child comes out empty.

I've tried it without http call (var array = [array]), and it works fine, but not with the http call.

Any tips on how to fix this?

Here's the controllers:

.controller('appCtrl',['$scope', 'SearchService','fair', function($scope, SearchService, fair){

  $scope.data = SearchService;
  $scope.datafairs = $scope.data.flatexhibitors;
  console.log($scope.datafairs);
}])

.controller('ChildController',['$scope', 'exhibitor', '$filter', function($scope, exhibitor, $filter){

  $scope.$watch(function() { return $scope.fair; }, function(newVal) { 
    $scope.fairs = newVal;
    console.log($scope.fairs);
    $scope.chosenexhibitor = $filter("filter")($scope.fairs, {'slug':exhibitor}, true);
  }, true);


}])

The service:

.factory("SearchService", function($http) {  

  var service = {
    flatexhibitors : [],
    datafairs : [],
    getAllExhibitors : function (wop) {
      var searchindex = wop;
        console.log(searchindex);
        var url = '../register/backend/databaseconnect/getexhibitors.php';
        var config = {
            params: {
                search: searchindex
            },
            cache:true
        };
        $http.get(url, config).then(function (data) {
          service.datafairs = data.data.rows;
          for (var i in service.datafairs) {
            service.flatexhibitors.push(service.datafairs[i].doc);
          };
          return service.flatexhibitors;
        });
    }   
  }
  return service;

})

And the states:

.config(function($stateProvider) {


  $stateProvider.state('berliner', {
    url: '/berlinerliste',
    params : {search: 'Berliner 2017'},
    resolve: {
      fair: function(SearchService, $stateParams) {
        return SearchService.getAllExhibitors($stateParams.search);
      }
    },
    views: {
      'header': {   
        templateUrl: 'header.htm'   
      },
      'main':{    
        templateUrl: 'bl2017.htm',
        controller: 'appCtrl'
      }    
    }
  })

  .state('berliner.exhibitor', {
    url: '/{id}', 
    resolve: {
      exhibitor: function($stateParams) {
        var slug = $stateParams.id;
        return slug;
      }
    },
    views: {
      'header': {   
        templateUrl: 'header.htm'   
      },
      'wop':{    
        templateUrl: 'exhibitor.htm',
        controller: 'ChildController'
      }    
    }
  })

})

I've managed to replicate the issue in a Plunkr.

Eric Mitjans
  • 2,149
  • 5
  • 40
  • 69

1 Answers1

1

Change the getAllExhibitors to return a promise like below:

getAllExhibitors : function (wop) {
  var searchindex = wop;
    console.log(searchindex);
    var url = '../register/backend/databaseconnect/getexhibitors.php';
    var config = {
        params: {
            search: searchindex
        },
        cache:true
    };
    return $http.get(url, config).then(function (data) {
      service.datafairs = data.data.rows;
      for (var i in service.datafairs) {
        service.flatexhibitors.push(service.datafairs[i].doc);
      };
      return service.flatexhibitors;
    });
}
CodeWarrior
  • 2,721
  • 3
  • 18
  • 18
  • 1
    This is an anti-pattern ... see : [What is the explicit promise construction antipattern and how do I avoid it?](https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it) – charlietfl Jul 29 '17 at 16:41
  • 1
    Thank you for your comment. It is only an anti pattern if there is no aggregation/conversion logic performed on the response before resolving the response. For instance, if in the above code, we resolved the response immediately on success without performing any computation on the response, it could have been considered an anti pattern and be replaced by a simple return on the $http call. But, in this case, we have looping logic that needs to be performed on the response before it is resolved. This warrants the need for using $q.defer() – CodeWarrior Jul 29 '17 at 16:50
  • 1
    No it doesn't. Can manipulate easily in `then()` the same way and simply do `return service.flatexhibitors`. There is no need for the additional deferred in this case and it clearly fits the anti-pattern as shown – charlietfl Jul 29 '17 at 16:55
  • 1
    Keep in mind that `then()` itself also returns a promise – charlietfl Jul 29 '17 at 16:58
  • I included it in my app and it works flawless, thank you guys! @CodeWarrior – Eric Mitjans Jul 29 '17 at 21:08