0

With all the examples of services, factories, using $scope, using Controller as, I'm getting a bit confused. I have a simple ng-if expression that's returning undefined because the data to evaluate isn't ready yet:

<div ng-if="ctrl.AlreadyBoughtShoe('ShoeId')"> ... </div>

  ...

<script>
  (function() {

    var MyApp = angular.module("MyApp", []);

    MyApp.controller("MyAppController", function($http, $timeout, ShoeService) {
      var x = this

      loadRemoteData();

      function loadRemoteData() {
        ShoeService.GetShoes().then(function(Shoes){
          applyRemoteData(Shoes); 
        });
      }

      function applyRemoteData(Shoes) {
        x.Shoes = Shoes;
      }

      // FAILS HERE - Get undefined on the array
      x.AlreadyBoughtShoe = function(shoeId) {
        for (var i = 0; i < x.Shoes.length; i++) {
            // Do stuff 
        }               
      } 
    });

    MyApp.service("ShoesService", function($http, $q){

      return({
        GetShoes: GetShoes        
      });

      function GetShoes() {

        var request = $http({
            method: "GET",
            url: /MyUrl",
            cache: false,
            headers: $myHeaders            
        });

        return( request.then(handleSuccess, handleError));
      }

      function handleError( response ) {

        if (!angular.isObject(response.data) || !response.data.message) {
          return( $q.reject( "An unknown error occurred." ) );
        }

        return( $q.reject(response.data.message) );

      }

      function handleSuccess( response ) {
        return( response.data );            
      }

    });
  })();

</script>

Also, if it's relevant, in this particular case it has nothing to do with shoes... and the data is a person object, and there's no ng-repeat going on, so the ID for the "shoe" is manually typed in. I jumbled up my actual code to simplify this so I can understand the best way to deal with it, but that ng-if needs to evaluate after the data is ready to be evaluated.

I'm not sure how to best used promises or whatever else I need in this style of format, which I found an example of somewhere on the web a while back.

scniro
  • 16,844
  • 8
  • 62
  • 106
user1447679
  • 3,076
  • 7
  • 32
  • 69

4 Answers4

1

This is happening because of the asynchronous nature of your service call in ShoeService. Your error is occurring due to code being called before x.Shoes = Shoes is resolved, essentially iterating over undefined. Try moving your logic into the then callback of your service. For example...

function loadRemoteData() {
   ShoeService.GetShoes().then(function(Shoes) {
       applyRemoteData(Shoes); 

       x.AlreadyBoughtShoe = function(shoeId) {
           for (var i = 0; i < x.Shoes.length; i++) {
               // Do stuff 
           }               
       } 
   });
}

You can probably move this to the end of applyRemoteData also if you wish. Either way you will need to execute this logic after you resolve x.Shoes

scniro
  • 16,844
  • 8
  • 62
  • 106
  • Thank you! I saw the possible duplicate. The ng-init is now discouraged, and with my particular setup it was hard to follow and translate into my situation. Not sure who voted to close, but wanted to say thanks. You saved me a headache regardless! – user1447679 Jul 14 '15 at 22:08
  • You are very welcome and I appreciate the feedback. Happy coding :) – scniro Jul 14 '15 at 22:12
0

You are right - when this code runs, x.Shoes is undefined. Change:

x.AlreadyBoughtShoe = function(shoeId) {
  for (var i = 0; i < x.Shoes.length; i++) {
      // Do stuff 
  }               
} 

to:

x.AlreadyBoughtShoe = function(shoeId) {
  for (var i = 0; i < (x.Shoes || []).length; i++) {
      // Do stuff
  }
}
mz3
  • 1,314
  • 11
  • 27
0

You have multiple options.

  1. Evaluate the ng-if to false by default until you receive the data. You keep your AlreadyBoughtShoe method but you first check if you have data. If you don't have data yet just return false. You won't have an error anymore and when your promise is resolved your HTML should reflect that.

  2. You can delay controller initialization until your promise is resolved.

Community
  • 1
  • 1
Stefan Ch
  • 329
  • 2
  • 7
0

Maybe setting semaphore or something simillar can help. Promise evaluates after some period of time, and setting variable to true after succesfull call may help. Then add that variable to the ng-if condition, which would evaluate function only when variable is true, so the promise returned.

Set variable to and condition, which would evaluate when both are true.

<div ng-if="ctrl.loaded && ctrl.AlreadyBoughtShoe('ShoeId')"> ... </div>

Then set variable to true on success ( by default is set to false because of javascript ).

  function loadRemoteData() {
        ShoeService.GetShoes().then(function(Shoes){
          x.loaded = true;
          applyRemoteData(Shoes); 
        });
      }

This may help.

changtung
  • 1,614
  • 15
  • 19