4

I have a Utils Service which is very heavy. I Want to use some of the functions defined in it on a particular user action. As this service is heavy I want to instantiate it lazily(on user action).

How do I achieve this?

Service

module.service('Utils', function (dep1, dep2) {
   this.method1 = function () {
      // do something
   }
   // other methods
});

Controller

module.controller('AppCtrl', function ($scope) {
    // I don't want to inject Utils as a dependency.

    $scope.processUserAction = function () {
       // If the service is not instantiated 
       // instantiate it and trigger the methods defined in it. 
    }
});

Markup

<div data-ng-controller="AppCtrl">
    <button data-ng-click="processUserAction()"> Click Me </button>
</div>
squiroid
  • 13,809
  • 6
  • 47
  • 67
Vinay K
  • 5,562
  • 1
  • 18
  • 27
  • 1
    what is "heavy" about that Service: is the JS-File large, so you want to lazy load it? is the Service instantiation taking a lot of time, so you want to delay it? is it a large "in memory" object, you want to save memory? depending on the answer, the solution will be very different. But I don't think you will gain anything by "not injecting" it, because loading and instantiating will be done anyway as soon as you add your service to your module, and it is a singleton anyway. – Reto Mar 28 '15 at 15:54
  • Instantiation of the service/factory happens only when we injected them as dependencies. By heavy means instantiating the service takes some time. To speed up the loading of the page I want to instantiate the service lazily. – Vinay K Mar 28 '15 at 16:22
  • adjusted the answer to show you the $injector service, this will solve your issue – Reto Mar 28 '15 at 16:56

1 Answers1

5

You can use $injector service to get services anywhere: https://docs.angularjs.org/api/auto/service/$injector. Inject the $injector into your controller, and whenever you need a service use:

This worked fine for me, the service is instantiated only on the $injector call, no error thrown.

 angular.module('yp.admin')

    .config(['$stateProvider', '$urlRouterProvider', 'accessLevels', '$translateWtiPartialLoaderProvider',
        function ($stateProvider, $urlRouterProvider, accessLevels, $translateWtiPartialLoaderProvider) {
            $stateProvider
                .state('admin.home', {
                    url: "/home",
                    access: accessLevels.admin,
                    views: {
                        content: {
                            templateUrl: 'admin/home/home.html',
                            controller: 'AdminHomeController'
                        }
                    }
                });
        }])
    .service('UtilsService', function() {
        console.log('utilsSerivce instantiated');
        return {
            call: function() {
                console.log('Util.call called');
            }
        };
    })

    .controller('AdminHomeController', ['$scope', '$rootScope', 'UserService', '$injector',
        function($scope, $rootScope, UserService, $injector) {
        $injector.get('UtilsService').call();
    }]);    

console gives me this:

stateChangeStart from:  to: admin.home
stateChangeSuccess from:  to: admin.home
utilsSerivce instantiated
Util.call called

If you want to delay loading the JS you should have a look at the ocLazyLoad Module: https://github.com/ocombe/ocLazyLoad. It addresses all sorts of lazy loading use cases and yours sounds like a good fit for it.

Reto
  • 3,107
  • 1
  • 21
  • 33
  • If we use ocLazyLoad, we need to include that module as a dependency. I don't want to do that. Just like $controller to instantiate a controller, is there a way to instantiate services/factories? – Vinay K Mar 28 '15 at 16:23
  • $inject will return the instance of the service/factory if it is already instantiated. If it is not instantiated, it throw an error. – Vinay K Mar 28 '15 at 16:58
  • use the $injector API: call $injector.has(). if false: call $injector.instantiate() – Reto Mar 28 '15 at 16:59
  • `instantiate(Type, [locals])` this is the signature of the instantiate function. The Type here should be a function. It can not take serviceName. – Vinay K Mar 28 '15 at 17:01
  • It should be provider rather than service or factory – Pankaj Parkar Mar 28 '15 at 17:13
  • I tried an example in our code. Added it to the answer above. This exact code worked without throwing, and instanciated the service lazily – Reto Mar 28 '15 at 17:16
  • @Reto, Your solution worked for me. Thanks for that. Instead of including $inject as a dependency I tried angular.injector().get('Utils'). That didn't work for me. Any Ideas? – Vinay K Mar 29 '15 at 04:30