Michael Mahony
Michael Mahony

Reputation: 1350

Angular function in service not seen as a function

I have been trying to move a bunch of code into a service instead of having it sit in the controller because other controllers in my application are going to need some of the same functionality. I have the following controller code:

JBenchApp.controller('CaseListCtrl', ['$scope', '$http', 'HoldState', 
  function ($scope, $http, HoldState) {

      //----------------------------------------------------------
      // Load the Calendar Data whent he department changes
      //----------------------------------------------------------

      $scope.getCalendarOnDeptChange = function () {
          // Get the dropdown into e
          var e = document.getElementById("deptSelect");

          // Set $scope.department to the text of the selected dropdown item --- MUST FIND BETTER ANGULAR METHOD
          $scope.department = e.options[e.selectedIndex].text;
          console.log($scope.department);
          $scope.getCalendar();
      };


      //----------------------------------------------------------
      // Load the calendar data
      //----------------------------------------------------------

      $scope.getCalendar = function () {
          // Retrieve calendar data
          HoldState.getCalendar($scope.lastDepartment, $scope.date, $scope.lastLawType, $scope.lastLocationID).then(function (data) {
              $scope.cases = data;
              $scope.$apply();
          });

          HoldState.setDepartment($scope.department);
      };

      //----------------------------------------------------------
      // Load the user's default settings
      //----------------------------------------------------------

      $scope.loadDefaults = function () {
          HoldState.getUserDefaults($scope.UserID).then(function (data) {
              $scope.UserDefaults = data;
          });

          $scope.defaultDepartment = $scope.UserDefaults.CourtRoom;
          $scope.defaultLawType = $scope.UserDefaults.LitigationCode;
          $scope.defaultLocationID = $scope.UserDefaults.LocID;
      };

      $scope.loadPaths = function () {
          HoldState.getTypeOfLaw().then(function (data) {
              $scope.lastLawType = data;
          });

          HoldState.getCourthouse().then(function (data) {
              $scope.lastLocationID = data;
          });

          HoldState.getDepartment().then(function (data) {
              $scope.lastDepartment = data;
          });
      };


      $scope.doAuthentication = function () {
          $scope.UserID = 'dpeng';
      };

      $scope.saveSequence = function () {

      };

      //----------------------------------------------------------
      // Initial processing
      //     Located here so that all functions are defined before 
      //     being called.
      // 1. Authenticate the user
      // 2. Get the default values
      // 3. Load the paths
      // 4. Get the list of departments
      // 5. Show the calendar.
      //----------------------------------------------------------
      $scope.doAuthentication();
      $scope.loadDefaults();
      $scope.loadPaths();

      HoldState.getDepartmentList($scope.lastLawType, $scope.lastLocationID).then(function (data) {
          $scope.departments = data;
      });

      $scope.getCalendar();

  }]);

I also have the following service code:

var StateService = angular.module('StateService', [])
.service('HoldState', function ($http) {
    this.setTypeOfLaw = function (a) { localStorage.setItem('LawType', a) };
    this.setCourthouse = function (a) { localStorage.setItem('Building', a) };
    this.setDepartment = function (a) { localStorage.setItem('Dept', a) };
    this.getTypeOfLaw = function () {
        var LT = localStorage.getItem('LawType');
        return LT;
    };
    this.getCourthouse = function () {
        var BLDG = localStorage.getItem('Building');
        return BLDG;
    };
    this.getDepartment = function () {
        var DEPT = localStorage.getItem('Dept');
        return DEPT;
    };

    this.setStatus = function (a) { localStorage.setItem('Status', a) };

    this.getStatus = function () {
        var STATUS = localStorage.getItem('Status');
        return STATUS;
    }

    //Begin default settings
    this.getUserDefaults = function (UserID) {
        var userDefaults = [];
        $http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
            .then(function (response) {
                userDefaults = response;
                var status = this.getStatus();

                // If the status is 0 then we have not yet navigated anywhere so we will need to set the path values to be
                // the same as the default. We do nothing if status is not 0 because it means we already have path values set
                if (status == 0) {
                    this.setTypeOfLaw(response.LitigationCode);
                    this.setCourthouse(response.LocID);
                    this.setDepartment(response.CourtRoom);
                }

            }, function (response) {
                console.log(response.status + " -- " + response.data + " -- " + response.statusText);
            });


        return userDefaults;
    };

When I call $scope.loadDefaults(); I get an error that says:

TypeError: HoldState.getUserDefaults(...).then is not a function
    at m.$scope.loadDefaults (http://localhost:54365/js/controllers.js:78:52)
    at new <anonymous> (http://localhost:54365/js/controllers.js:121:14)
    at Object.e [as invoke] (http://localhost:54365/js/angular.min.js:36:315)
    at x.instance (http://localhost:54365/js/angular.min.js:76:79)
    at http://localhost:54365/js/angular.min.js:59:85
    at q (http://localhost:54365/js/angular.min.js:7:428)
    at M (http://localhost:54365/js/angular.min.js:59:69)
    at g (http://localhost:54365/js/angular.min.js:51:409)
    at http://localhost:54365/js/angular.min.js:51:17
    at chrome-extension://ighdmehidhipcmcojjgiloacoafjmpfk/dist/hint.js:2071:22 <div ng-view="" class="view-frame ng-scope">(anonymous function) @ angular.min.js:102
angular.min.js:102 TypeError: Cannot read property 'getStatus' of undefined
    at controllers.js:311
    at angular.min.js:112
    at m.$eval (angular.min.js:126)
    at m.$digest (angular.min.js:123)
    at m.scopePrototype.$digest (chrome-extension://ighdmehidhipcmcojjgiloacoafjmpfk/dist/hint.js:1955)
    at m.$apply (angular.min.js:127)
    at m.scopePrototype.$apply (chrome-extension://ighdmehidhipcmcojjgiloacoafjmpfk/dist/hint.js:2018)
    at l (angular.min.js:81)
    at P (angular.min.js:85)
    at XMLHttpRequest.H.onload (angular.min.js:86)(anonymous function) @ angular.min.js:102

What have I done wrong? I am merely trying to cleanly get back the data from a web service through my Angular service.

Upvotes: 1

Views: 122

Answers (3)

Schlaus
Schlaus

Reputation: 19182

Take another look at the error:

HoldState.getUserDefaults(...).then is not a function
                               ^^^^

HoldState.getUserDefaults() doesn't return a promise, that's the problem.

To be able to consume the service like you are at the moment, tweak the method a little:

this.getUserDefaults = function (UserID) {
    return $http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
        .then(function (response) {
            var status = this.getStatus();

            // If the status is 0 then we have not yet navigated anywhere so we will need to set the path values to be
            // the same as the default. We do nothing if status is not 0 because it means we already have path values set
            if (status == 0) {
                this.setTypeOfLaw(response.LitigationCode);
                this.setCourthouse(response.LocID);
                this.setDepartment(response.CourtRoom);
            }    
        }, function (response) {
            console.log(response.status + " -- " + response.data + " -- " + response.statusText);
        });

};

Since promises are chainable, you are essentially returning the promise you get from $http.get(), which will be resolved with response. This should make it work.

Upvotes: 0

FesterCluck
FesterCluck

Reputation: 320

This should fix the problem

  $scope.loadDefaults = function () {
      HoldState.getUserDefaults($scope.UserID).then(function (data) {
          $scope.UserDefaults = data;
      }, function(errData) {
          //$scope.UserDefaults isn't going to get filled, so do some error handling here.
      });

      $scope.defaultDepartment = $scope.UserDefaults.CourtRoom;
      $scope.defaultLawType = $scope.UserDefaults.LitigationCode;
      $scope.defaultLocationID = $scope.UserDefaults.LocID;
  };

And

//Begin default settings
this.getUserDefaults = function (UserID) {
    //Here we return the promise
    return $http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
        .then(function (response) {
            userDefaults = response;
            var status = this.getStatus();

            // If the status is 0 then we have not yet navigated anywhere so we will need to set the path values to be
            // the same as the default. We do nothing if status is not 0 because it means we already have path values set
            if (status == 0) {
                this.setTypeOfLaw(response.LitigationCode);
                this.setCourthouse(response.LocID);
                this.setDepartment(response.CourtRoom);
            }
            //Here we fill the data variable
            return response;

        }, function (response) {
            console.log(response.status + " -- " + response.data + " -- " + response.statusText);
        });

};

Upvotes: 0

Neil S
Neil S

Reputation: 2304

getUserDefaults is the only method that really needs to be a promise, as you are making an async call to your api. So, inject $q into your service and then have that method return a promise.

this.getUserDefaults = function (UserID) {
    var userDefaults = [], deferred = $q.defer();
    $http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
        .then(function (response) {

            var status = this.getStatus();

            // If the status is 0 then we have not yet navigated anywhere so we will need to set the path values to be
            // the same as the default. We do nothing if status is not 0 because it means we already have path values set
            if (status == 0) {
                this.setTypeOfLaw(response.LitigationCode);
                this.setCourthouse(response.LocID);
                this.setDepartment(response.CourtRoom);
            }
            d.resolve(response);
        }, function (response) {
            console.log(response.status + " -- " + response.data + " -- " + response.statusText);
        });


    return deferred.promise;
};

you also should just use the getters as getters, and not try to treat them as promises. i.e.

$scope.loadPaths = function () {
      $scope.lastLawType = HoldState.getTypeOfLaw();

      $scope.lastLocationID = HoldState.getCourthouse();

      $scope.lastDepartment = HoldState.getDepartment();
  };

Upvotes: 1

Related Questions