1

I am trying to understand Angularjs behaviors. I am building a web-app, and I want the CurrentUser's info be shared among all the app components. To do that, I have created a CurrentUserController bound to $rootScope. This controller is used by a user directive utilized in the body html element, so that it is globally accessible and it's created just one time.

app.controller('CurrentUserController', function ($rootScope) 
  {
   // initialization
   $rootScope.userCtrl = self; //<- MAKE IT GLOBAL
   this.islogged=false;
   this.name="";
   var self = this;
   // functions
   this.isLogged = function() 
        { return self.islogged; };

   this.setLoggedIn = function(credentials) 
        { self.islogged = true; }; 

   this.setLoggedOut = function() 
        { self.islogged = false; };                 
  }
);

app.directive('currentUser', function() {
  return {
    controller:'CurrentUserController'
  };
 })

and then my html page

<html>
...
<body current-user> 
...
</body>
</html>

However I read that Services should be used to share data between controllers, since they are singleton. So my question is: is my approach wrong, or it is equivalent as I utilized services?

Moreover, right now I can utilize the directive ng-switch calling $rootScope.userCtrl functions, like this:

<div id="nav-right-side" class="navbar-right" ng-switch on="userCtrl.isLogged()">
            <div ng-switch-when="false">
                <login-button></login-button>
            </div>
        <div ng-switch-when="true">
                <loggedin-button></loggedin-button>
            </div>                      

        </div>

If I utilize services, would I still be able to do that? Thank you

2 Answers 2

3

The $rootScope is indeed shared across all the app and it is also best to store models into services.

Why bother with services ? Because of the $digest cycle. Each time a watched value is modified, the digest is triggered. In angular, by default the digest is a loop that goes down all your scope from the $rootScope down to its leafs. On each element, it has to get if the value has been modified or not to update the view accordingly. This is pretty expensive, and it is the cause of why angular can be slow on big applications. Keeping the scope as light as possible is how you can build complex apps in angular. That's why storing things is always better in services, you do not pollute the scope with data you could put somewhere else.

That being said, auth is peculiar because you want to access the same data from the view and services. You can store it in the $rootScope as Asta puts it but I do not think that is consistant with best practices. This is opinionated

What can be done is creating a service that will hold you model and share it through a controller to have access to it from both the view and the other services/models.

Session.js

function Session(){
     var 
       self = this,

       _islogged=false,
       _name = '';
    // functions
    this.isLogged = function() { 
        return self.islogged; 
    };

    this.setLoggedIn = function() { 
         self.islogged = true; 
    }; 

    this.setLoggedOut = function() { 
        self.islogged = false; };                 
    }

    // GetUsername, setUsername ... Whatever you need
}
angular
    .module('app')
    .service('Session', Session);

rootController.js

function rootController(Session){
    // share the Session Service with the $scope
    // this.session is like $scope.session when using the controllerAS syntax.
    this.session = Session;
}

angular
    .module('app')
    .controller('rootController', rootController);

I would suggest you take a look at these articles:

Sign up to request clarification or add additional context in comments.

11 Comments

Even if all the controllers share the same service, in this way I have to create a new instance of rootController in every (let's say) div or directive that needs it. Am i right?
No, you should not have too unless the directive have an isolated scope. In angular all child scope inherit from their parent scope. When not using the controllerAs syntax you would have to use $scope.parent to access the parent scope. With controller as, provided the different view are siblings you can access the session object from the view with rootController.session. You do not have to instantiate this multiple time. Putting it at the root of your app should be enough
So, as best practice, I should still use the controller in the body element, as my approach, but I should just externalize the behavior in a service
If you do it this way you will be using the login controller for all views and it simply initializes the session. I think you would be better to call a service directly and so avoid the extra controller layer.
@Delac Yes that is the way to go.
|
0

Your best to use a Service to share data as you mention. In your approach you've used a Controller in a way that its not really intended.

You can call your controller from your HTML by using ng-controller so something like the following should work. This would be useful for a Login view for example or a logout directive.

<div ng-controller="userCtrl">
    <div id="nav-right-side" class="navbar-right" ng-switch on="isLogged()">
       <div ng-switch-when="false">
           <login-button></login-button>
       </div>
       <div ng-switch-when="true">
           <loggedin-button></loggedin-button>
       </div>                      
    </div>
</div>

In order to have your session available globally for use elsewhere you can use a service which you can initialise from your app. The session data can be added to $rootScope which you can then reference from any view or controller.

Service

angular.module('app').service('session', function($rootScope) {
    $rootScope.sessionData.loggedIn = true
    // extra logic etc..
});

Main App

angular.run(session)
angular.module('app').run(function(session) {});

Then to reference the variable from your view

<div id="nav-right-side" class="navbar-right" ng-switch on="sessionData.isLoggedIn">

Note: its good practice to use an object with scope variables to help avoid issues with inheritance.

5 Comments

but in this way userCtrl is accessible just in that div.... I'd like to use it from whatever I am
Ah yeah OK. I was imagining this controller on a particular view as this is what controllers are intended for. In your case the User logic can be inside a service and to instantiate it you can call it from your main app.run function. So instead of isLogged() you would set a $rootScope var indicating if they are logged in.
Updated my answer for use outside the directive
Then, to use a service in a controller, should I I use $rootScope.sessionData, or I should inject the service as .controller('controller', [function(session) {...}]) ?
If you want to call any of the functions in the service then you would inject the service in to the controller. If you just want to get the sessionData from $rootScope then you can just reference that directly. If you have an action that causes a user to become logged out for example (say, from an external action outwith the login/logout controller) then you can update $rootScope from that service or you could use events.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.