Reusable loading ,Cannot read property 'hide' of undefined


#1

Hi there. I’m trying to find a way to create a reusable service for loading. I’m creating an that uses an API to get data so loading should work properly. I read some topics and decided to go with one that was giving the following service:

var services = angular.module('services.common', ['ionic']);
services.factory('LoadingService', function($rootScope, $ionicLoading) {
  return {
        show : function() {

            $rootScope.loading = $ionicLoading.show({

              content: '<i class="icon ion-looping"></i> Loading',
              animation: 'fade-in',
              showBackdrop: true,
              maxWidth: 200,
              showDelay: 10
            });
        },

        hide : function(){
            $rootScope.$ionicLoading.hide();
        }
    };
});

And my controller is something like:

var app = angular.module('controllers.search', ['ionic']);

app.controller('myCtrl', function($scope, $state, $http, myService, $ionicLoading, LoadingService) {

    // ...

    $scope.doAdvancedSearch = function advancedSearch(search) {


        LoadingService.show();

        var myList = [];

        // ...

        var successCallback = function (){
            $state.go('results');
        };

        var errorCallback = function (){
            alert("Error!.");
        };

        SearchService.search(search, ...,  successCallback, errorCallback);

        LoadingService.hide();

    };

});

The error I’m getting is: Cannot read property ‘hide’ of undefined.


#2

You declared your service as “LoadingService”, but you are trying to inject it as “LoaderService”, which would be undefined.


#3

Well I have already fixed this issue (forgot to update the code, sorry for that).

But the error remains, and it’s TypeError: Cannot read property ‘hide’ of undefined at Object.hide…

I think it complains about $rootScope.loading.$ionicLoading.hide();.

(Ps. The loading shows up. it’s just that it does not hide)


#4

Actually, your service could use some refactoring. You can remove $rootScope.loading = and just let your show function directly call $ionicLoading.show. As it stands, your show function is assigning the result of $ionicLoading to a $rootScope variable called loading, which isn’t necessary. And then your hide function should also be calling $ionicLoading.hide directly, not as a member of $rootScope.
Maybe something more like this:

 show : function() {
           $ionicLoading.show({
              content: '<i class="icon ion-looping"></i> Loading',
              animation: 'fade-in',
              showBackdrop: true,
              maxWidth: 200,
              showDelay: 10
            });
        },
        hide : function(){
            $ionicLoading.hide();
        }```

#5

All of that being said, you could just set the $ionicLoading defaults to use the template you want, then just show/hide directly from your controllers without needing to wrap it up inside another service.


#6

This code looks better and more clear. I tried it but now the loading does not show up at all. Any ideas?

Ps. Now I do not getting an error (but the loading doesn’t show up.


#7

Can you post what your current controller and service look like? Though I would still suggest utilizing $ionicLoadingConfig when you init your app, and ionicLoading directly (without wrapping inside your own service), unless you have a specific reason otherwise.


#8

Well I tried to set it like:

app.constant('$ionicLoadingConfig', {
  template: 'Default Loading Template...'
}),

but still nothing. But I prefer to have a service for this since I’m gonna use it on many controllers.

Well I’m currently trying to include it to an ng-click function. So it’s something like:

app.controller('SearchCtrl', function(appConfig, $scope, $state, $http, SearchService, LoadingService) {

    $scope.doAdvancedSearch = function advancedSearch(search) {

        LoadingService.show();

        ...

        var successCallback = function (){
            $state.go('...');
        };

        var errorCallback = function (){
            alert("Error! Getting...");
        };

        SearchService.search(search, successCallback, errorCallback);

        LoadingService.hide();

    };

});

#9

$ionicLoading is already a service, which you can include from as many controllers as you need. So there’s really no need to wrap it in an extra service. It’s already ‘reusable’. Inside any controller, you can inject $ionicLoading, then simply call $ionicLoading.show() and $ionicLoading.hide() directly from your controller. You gain nothing but complexity by adding it to another service, UNLESS you have a ‘default’ loading view you want to show in many places, as well as another loading view with different options that you also want to call from multiple places.
Also, you probably want to make sure to wait to hide the loading until you have actually resolved your search. Your controller looks like you are hiding it before it has a chance to display.


#10

Alright! I think I got it now! Thanks a lot for spending time on this one!

Like you said: Also, you probably want to make sure to wait to hide the loading until you have actually resolved your search. Your controller looks like you are hiding it before it has a chance to display.

I misplaced where i had to hide the loading. Now it works whether it’s a service or not! Thank you!


#11

Dont use $rootScope.$ionicLoading.hide(); in service use $ionicLoading.hide();