Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I inject $scope into a factory in Angular?

I have a factory that needs to listen for a broadcast event. I injected $scope into the factory so I could use $scope.$on. But as soon as I add $scope to the parameter list I get an injector error.

This works fine:

angular.module('MyWebApp.services')
    .factory('ValidationMatrixFactory', ['$rootScope', function($rootScope) {
        var ValidationMatrixFactory = {};
        return ValidationMatrixFactory;
    }]);

This throws an injector error:

angular.module('MyWebApp.services')
    .factory('ValidationMatrixFactory', ['$scope', '$rootScope', function($scope, $rootScope) {
        var ValidationMatrixFactory = {};
        return ValidationMatrixFactory;
    }]);

Why can't I inject $scope into a factory? And if I can't, do I have any way of listening for events other than using $rootScope?

like image 723
Legion Avatar asked Feb 07 '23 02:02

Legion


2 Answers

Because $scope is used for connecting controllers to view, factories are not really meant to use $scope.

How ever you can broadcast to rootScope.

$rootScope.$on()
like image 163
Niles Tanner Avatar answered Feb 08 '23 15:02

Niles Tanner


Even though you can't use $scope in services, you can use the service as a 'store'. I use the following approach inspired on AltJS / Redux while developing apps on ReactJS.

I have a Controller with a scope which the view is bound to. That controller has a $scope.state variable that gets its value from a Service which has this.state = {}. The service is the only component "allowed" (by you, the developer, this a rule we should follow ourselves) to touch the 'state'.

An example could make this point a bit more clear

(function () {
    'use strict';
    angular.module('app', ['app.accounts']);  
  
    // my module... 
    // it can be defined in a separate file like `app.accounts.module.js`
    angular.module('app.accounts', []);
 
    angular.module('app.accounts')
        .service('AccountsSrv', [function () {
            var self = this;

            self.state = {
                user: false
            };
            
            self.getAccountInfo = function(){
              var userData = {name: 'John'};   // here you can get the user data from an endpoint
              self.state.user = userData;      // update the state once you got the data
            };
        }]);
    
    // my controller, bound to the state of the service
    // it can be defined in a separate file like `app.accounts.controller.js`
    angular.module('app.accounts')
        .controller('AccountsCtrl', ['$scope', 'AccountsSrv', function ($scope, AccountsSrv) {
            $scope.state = AccountsSrv.state;
          
            $scope.getAccountInfo = function(){
                // ... do some logic here
                // ... and then call the service which will 
                AccountsSrv.getAccountInfo();
            }
        }]);
})();
<script src="https://code.angularjs.org/1.3.15/angular.min.js"></script>
<div ng-app="app">
  <div  ng-controller="AccountsCtrl">
    Username: {{state.user.name ? state.user.name : 'user info not available yet. Click below...'}}<br/><br/>
    <a href="javascript:void(0);" ng-click="getAccountInfo()">Get account info</a>
  </div>
</div>

The benefit of this approach is you don't have to set $watch or $on on multiple places, or tediously call $scope.$apply(function(){ /* update state here */ }) every time you need to update the controller's state. Also, you can have multiple controllers talk to services, since the relationship between components and services is one controller can talk to one or many services, the decision is yours. This approach focus on keeping a single source of truth.

I've used this approach on large scale apps... it has worked like a charm.

I hope it helps clarify a bit about where to keep the state and how to update it.

like image 32
JorgeObregon Avatar answered Feb 08 '23 15:02

JorgeObregon