Discitur Project
Another aspect always related to authentication: how to intercept the user login and how to react to this event.
I would add a few more aspect: the login, in Discitur application, is designed to fit in a modal window, which can be called from several parts of the application. Eg., already in this sprint (which is the first to use the authentication) the login is called from at least three different points:
- from the main navigation bar via the link “Login”
- from Comment form inserted at the end of each lesson (lessons are visible to all, but commenting is a feature that requires authentication)
- from the “Ratings” to enter its ownassessment
Over the next sprint there will certainly be other chances that will involve an authenticated access. So what I want to avoid is to “pollute” the various controls (which need authentication) with code for opening the modal window, which would make the controller highly-coupled with each other.
To do this, I lean to the Angular event management, in particular by launching a broadcast of the login event from $rootScope. A quick thought: use the $rootScope to be sure that the event reaches the main controller from which the login modal window is generated. If I use a simple $scope of the starting controller, and the controller was not the parent scope of what I want to achieve, I would risk launching the event into nothing.
So, if in a generic controller I need the user login, I implement a method that throw my event:
At this point, somewhere, the event will be run and the login window open. In this article, rather than on aspects of UI or interaction with back-end, I wanted to focus on the management of authentication data and how it is propagated to the various controllers that manage it. These are the steps followed:
- the login is invoked and proceeds to perform the server-side authentication
- if the login is successful, the property “user” (of the authentication service) is set containing all user information
- this object, being within a service (which by its nature is a singleton), is unique and its updates are visible everywhere you face injection of Service
- inside the controller which need to know the state of user authentication, perform a watcher of the properties of user interests, in particular user.isLogged
I personally find this approach useful, but not quite elegant, in particular I would rather not write a watcher of the same type in each controller; conversely I wanted to inject my authentication service and check the properties of the user, to see if the authentication has taken place or not.
Making a comparison between what I did and what I would like to do:
What I Did
Controller:
Template:
What I wanted to do
Template:
What I have done is a result of a digest cycle and the logic that uses Angular to control any changes. Angular is running a “dirty checking” with each digest cycle (attached to the event loop of the browser). The cycle of checking brushes all scopes looking for value changes, the problem is that the services are not brushed independently from angular and therefore are required watcher who are nothing but the functions that the cycle of periodic digest of angular calls for update the various scopes. The cycle is clear to me and the end is ok, but I would have liked if the cycle Digest had brushed services are also injected by the different scope.
Review
Doing further research and, most importantly, having deepened this post I understood better how the Angular digest cycle works. Angular checks only primitive types, and, in the case of non-primitive objects watcher turned on, Angular does dirty checking of all primitive properties of the object, as reported by the method of comparison used: angular.equal.
Controller:
Template:
With the above code, Angular defines a $watch on the “scope.local.user.isLogged” and, to make it, it identifies the scope’s property “scope.local.user”, which is set with reference to the object of the service user. So with the following code:
Controller:
Template:
Angular, set a watcher of the following type:
In this way you are able to put in the binding properties of a service, without having to resort to an explicit watcher in the controller.
first reflection:
The code that I wanted to write was initially mistaken for the simple fact that the watcher will not stick directly to services (maybe one day they will), but look within their scope.
second reflection:
a reading which confirmed all my insights was this: http://stsc3000.github.io/blog/2013/10/26/a-tale-of-frankenstein-and-binding-to-service-values-in-angular-dot-js/
If only I had found it earlier…
In any case, it is certain that from now on I will write code more aware!
Third reflection:
of this watcher opens a topic, that of performance, quite important. In complex applications it is easy to get to thousands and thousands of these watcher running at each cycle of the digest…
I do not now, but a smart thing to do is to disconnect the watcher when it is needed most. Readings fast (but interesting) I’ve done about it:
http://angular-tips.com/blog/2013/08/removing-the-unneeded-watches/
http://www.bennadel.com/blog/2480-Unbinding-watch-Listeners-In-AngularJS.htm.
Comments