Menu Search
Jump to the content X X
Smashing Conf New York

We use ad-blockers as well, you know. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. upcoming SmashingConf Barcelona, dedicated to smart front-end techniques and design patterns.

AngularJS’ Internals In Depth

AngularJS presents a remarkable number of interesting design choices in its code base. Two particularly interesting cases are the way in which scopes work and how directives behave.

The first thing anyone is taught when approaching AngularJS for the first time is that directives are meant to interact with the DOM, or whatever manipulates the DOM for you, such as jQuery (get over jQuery already1!). What immediately becomes (and remains) confusing for most, though, is the interaction between scopes, directives and controllers.

After the confusion sets in, you start learning about the advanced concepts: the digest cycle, isolate scopes, transclusion and the different linking functions in directives. These are mind-blowingly complex as well. I won’t cover directives in this article, but they’ll be addressed in its follow-up.

This article will navigate the salt marsh that is AngularJS scopes and the lifecycle of an AngularJS application, while providing an amusingly informative, in-depth read.

(The bar is high, but scopes are sufficiently hard to explain. If I’m going to fail miserably at it, at least I’ll throw in a few more promises I can’t keep!)

If the following figure looks unreasonably mind-bending, then this article might be for you.

01-mindbender2

(Image credit3) (View large version4)

(Disclaimer: This article is based on AngularJS version 1.3.05.)

AngularJS uses scopes to abstract communication between directives and the DOM. Scopes also exist at the controller level. Scopes are plain old JavaScript objects (POJO) that AngularJS does not heavily manipulate. They only add a bunch of “internal” properties, prefixed with one or two $ symbols. The ones prefixed with $$ aren’t necessary as frequently, and using them is often a code smell, which can be avoided by having a deeper understanding of the digest cycle.

What Kind Of Scopes Are We Talking About? Link

In AngularJS slang, a “scope” is not what you might be used to when thinking about JavaScript code or even programming in general. Usually, scopes are used to refer to the bag in a piece of code that holds the context, variables and so on.

(In most languages, variables are held in imaginary bags, which are defined by curly braces ({}) or code blocks. This is known as “block scoping6.” JavaScript, in contrast, deals in “lexical scoping7,” which pretty much means that the bags are defined by functions, or the global object, rather than by code blocks. Bags may contain any number of smaller bags. Each bag can access the candy (sweet, sweet variables) inside its parent bag (and in its parent’s parent, and so on), but they can’t poke holes in smaller, or child, bags.)

As a quick and dirty example, let’s examine the function below.

function eat (thing) {
   console.log('Eating a ' + thing);
}

function nuts (peanut) {
   var hazelnut = 'hazelnut';

   function seeds () {
      var almond = 'almond';
      eat(hazelnut); // I can reach into the nuts bag!
   }

   // Almonds are inaccessible here.
   // Almonds are not nuts.
}

I won’t dwell on this matter8 any longer, because these are not the scopes people refer to when talking about AngularJS. Refer to “Where Does this Keyword Come From?9” if you’d like to learn more about scopes in the context of the JavaScript language.

Scope Inheritance in AngularJS Link

Scopes in AngularJS are also context, but on AngularJS’ terms. In AngularJS, a scope is associated with an element (and all of its child elements), while an element is not necessarily directly associated with a scope. Elements are assigned a scope is one of the following three ways.

The first way is if a scope is created on an element by a controller or a directive (directives don’t always introduce new scopes).

<nav ng-controller='menuCtrl'>

Secondly, if a scope isn’t present on the element, then it’s inherited from its parent.

<nav ng-controller='menuCtrl'>
   <a ng-click='navigate()'>Click Me!</a> <!-- also <nav>'s scope -->
</nav>

Thirdly, if the element isn’t part of an ng-app, then it doesn’t belong to a scope at all.

<head>
   <h1>Pony Deli App</h1>
</head>
<main ng-app='PonyDeli'>
   <nav ng-controller='menuCtrl'>
      <a ng-click='navigate()'>Click Me!</a>
   </nav>
</main>

To figure out an element’s scope, try to think of elements recursively inside out following the three rules I’ve just outlined. Does it create a new scope? That’s its scope. Does it have a parent? Check the parent, then. Is it not part of an ng-app? Tough luck — no scope.

You can (and most definitely should) use the magic of developer tools to easily figure out the scope of an element.

Pulling Up AngularJS’ Internal Scope Properties Link

I’ll walk through a few properties in a typical scope to introduce certain concepts, before moving on to explaining how digests work and behave internally. I’ll also let you in on how I get to these properties. First, I’ll open Chrome and navigate to the application I’m working on, which is written in AngularJS. Then, I’ll inspect an element and open the developer tools.

(Did you know that $0 gives you access to the last selected element10 in the “Elements” pane? $1 gives you access to the previously selected element, and so on. I prognosticate that you’ll use $0 the most, particularly when working with AngularJS.)

For any given DOM element, angular.element wraps that in either jQuery11 or jqLite, jQuery’s own little mini version12. Once it’s wrapped, you get access to a scope() function that returns — you guessed it! — the AngularJS scope associated with that element. Combining that with $0, I find myself using the following command quite often.

angular.element($0).scope()

(Of course, if you know that you’ll be using jQuery, then $($0).scope() will work just the same. And angular.element works every time, regardless of whether jQuery is available.)

Then, I’m able to inspect the scope, assert that it’s the scope I expected, and assert whether the property’s values match what I was expecting. Super-useful! Let’s see what special properties are available in a typical scope, those prefixed with one or more dollar signs.

for(o in $($0).scope())o[0]=='$'&&console.log(o)

That’s good enough. I’ll go over all of the properties, clustering them by functionality, and go over each portion of AngularJS’ scoping philosophy.

Examining A Scope’s Internals In AngularJS Link

Below, I’ve listed the properties yielded by that command, grouped by area of functionality. Let’s start with the basic ones, which merely provide scope navigation.

No surprises here. Navigating scopes like this would be utter nonsense. Sometimes accessing the $parent scope might seem appropriate, but there are always better, less coupled, ways to deal with parental communication than by tightly binding people scopes together. One such way is to use event listeners, our next batch of scope properties!

Event Model in AngularJS Scope Link

The properties described below enable us to publish events and subscribe to them. This is a pattern known as PubSub20, or just events.

  • $$listeners21
    event listeners registered on the scope
  • $on(evt, fn)22
    attaches an event listener fn named evt
  • $emit(evt, args)23
    fires event evt, roaring upward on the scope chain, triggering on the current scope and all $parents, including the $rootScope
  • $broadcast(evt, args)24
    fires event evt, triggering on the current scope and all of its children

When triggered, event listeners are passed an event object and any arguments passed to the $emit or $broadcast function. There are many ways in which scope events can provide value.

A directive might use events to announce that something important has happened. Check out the sample directive below, where a button can be clicked to announce that you feel like eating food of some type.

angular.module('PonyDeli').directive('food', function () {
   return {
      scope: { // I'll come back to directive scopes later
         type: '=type'
      },
      template: '<button ng-click="eat()">I want to eat some {{type}}!</button>',
      link: function (scope, element, attrs) {
         scope.eat = function () {
            letThemHaveIt();
            scope.$emit('food.order, scope.type, element);
         };

         function letThemHaveIt () {
            // Do some fancy UI things
         }
      }
   };
});

I namespace my events, and so should you. It prevents name collisions, and it makes clear where events originate from or what event you’re subscribing to. Imagine you have an interest in analytics and want to track clicks on food elements using Mixpanel25. That would actually be a reasonable need, and there’s no reason why that should be polluting your directive or your controller. You could put together a directive that does the food-clicking analytics-tracking for you, in a nicely self-contained manner.

angular.module('PonyDeli').directive('foodTracker', function (mixpanelService) {
   return {
      link: function (scope, element, attrs) {
         scope.$on('food.order, function (e, type) {
            mixpanelService.track('food-eater', type);
         });
      }
   };
});

The service implementation is not relevant here, because it would merely wrap Mixpanel’s client-side API. The HTML would look like what’s below, and I’ve thrown in a controller to hold all of the food types that I want to serve in my deli. The ng-app26 directive helps AngularJS to auto-bootstrap my application as well. Rounding out the example, I’ve added an ng-repeat directive so that I can render all of my food without repeating myself; it’ll just loop through foodTypes, available on foodCtrl’s scope.

<ul ng-app='PonyDeli' ng-controller='foodCtrl' food-tracker>
   <li food type='type' ng-repeat='type in foodTypes'></li>
</ul>

angular.module('PonyDeli').controller('foodCtrl', function ($scope) {
   $scope.foodTypes = ['onion', 'cucumber', 'hazelnut'];
});

The fully working example is hosted on CodePen27.

That’s a good example on paper, but you need to think about whether you need an event that anyone can subscribe to. Maybe a service will do? In this case, it could go either way. You could argue that you’ll need events because you don’t know who else is going to subscribe to food.order, which means that using events would be more future-proof. You could also say that the food-tracker directive doesn’t have a reason to be, because it doesn’t interact with the DOM or even the scope at all other than to listen to an event, which you could replace with a service.

Both thoughts would be correct, in the given context. As more components need to be food.order-aware, then it might feel clearer that events are the way to go. In reality, though, events are most useful when you actually need to bridge the gap between two (or more) scopes and other factors aren’t as important.

As we’ll see when we inspect directives more closely in the upcoming second part of this article, events aren’t even necessary for scopes to communicate. A child scope may read from its parent by binding to it, and it may also update those values.

(There’s rarely a good reason to host events to help children communicate better with their parents.)

Siblings often have a harder time communicating with each other, and they often do so through a common parent. That generally translates into broadcasting from $rootScope and listening on the siblings of interest, as below.

<body ng-app='PonyDeli'>
   <div ng-controller='foodCtrl'>
      <ul food-tracker>
         <li food type='type' ng-repeat='type in foodTypes'></li>
      </ul>
      <button ng-click='deliver()'>I want to eat that!</button>
   </div>
   <div ng-controller='deliveryCtrl'>
      <span ng-show='received'>
         A monkey has been dispatched. You shall eat soon.
      </span>
   </div>
</body>

angular.module('PonyDeli').controller('foodCtrl', function ($rootScope) {
   $scope.foodTypes = ['onion', 'cucumber', 'hazelnut'];
   $scope.deliver = function (req) {
      $rootScope.$broadcast('delivery.request', req);
   };
});

angular.module('PonyDeli').controller('deliveryCtrl', function ($scope) {
   $scope.$on('delivery.request', function (e, req) {
      $scope.received = true; // deal with the request
   });
});

This one is also on CodePen28.

Over time, you’ll learn to lean towards events or services accordingly. I could say that you should use events when you expect view models to change in response to event and that you ought to use services when you don’t expect changes to view models. Sometimes the response is a mixture of both: an action triggers an event that calls a service, or a service that broadcasts an event on $rootScope. It depends on the situation, and you should analyze it as such, rather than attempting to nail down the elusive one-size-fits-all solution.

If you have two components that communicate through $rootScope, then you might prefer to use $rootScope.$emit (rather than $broadcast) and $rootScope.$on. That way, the event would only spread among $rootScope.$$listeners, and it wouldn’t waste time looping through every child of $rootScope, which you just know won’t have any listeners for that event. Below is an example service using $rootScope to provide events without limiting itself to a particular scope. It provides a subscribe method that allows consumers to register event listeners, and it might do things internally that trigger that event.

angular.module('PonyDeli').factory("notificationService", function ($rootScope) {
   function notify (data) {
      $rootScope.$emit("notificationService.update", data);
   }

   function listen (fn) {
      $rootScope.$on("notificationService.update", function (e, data) {
         fn(data);
      });
   }

   // Anything that might have a reason
   // to emit events at later points in time
   function load () {
      setInterval(notify.bind(null, 'Something happened!'), 1000);
   }

   return {
      subscribe: listen,
      load: load
   };
});

You guessed right! This one is also on CodePen29.

Enough events-versus-services banter. Shall we move on to some other properties?

Digesting Changesets Link

Understanding this intimidating process is the key to understanding AngularJS.

AngularJS bases its data-binding features in a dirty-checking loop that tracks changes and fires events when these change. This is simpler than it sounds. No, really. It is! Let’s quickly go over each of the core components of the $digest cycle. First, there’s the scope.$digest method, which recursively digests changes in a scope and its children.

  1. $digest()30
    executes the $digest dirty-checking loop
  2. $$phase31
    current phase in the digest cycle, one of [null, '$apply', '$digest']

You need to be careful about triggering digests, because attempting to do so when you’re already in a digest phase would cause AngularJS to blow up in a mysterious haze of unexplainable phenomena. In other words, pinpointing the root cause of the issue would be pretty hard.

Let’s look at what the documentation says32 about $digest.

Processes all of the watchers3633 of the current scope and its children. Because a watcher34’s listener can change the model, the $digest()423735 keeps calling the watchers3633 until no more listeners are firing. This means that it is possible to get into an infinite loop. This function will throw 'Maximum iteration limit exceeded.' if the number of iterations exceeds 10.

Usually, you don’t call $digest()423735 directly in controllers38 or in directives4139. Instead, you should call $apply()40 (typically from within a directives4139), which will force a $digest()423735.

So, a $digest processes all watchers, and then processes the watchers that those watchers trigger, until nothing else triggers a watch. Two questions remain to be answered for us to understand this loop.

  • What the hell is a “watcher”?!
  • What triggers a $digest?!

The answers to these questions vary wildly in terms of complexity, but I’ll keep my explanations as simple as possible so that they’re clear. I’ll begin talking about watchers and let you draw your own conclusions.

If you’ve read this far, then you probably already know what a watcher is. You’ve probably used scope.$watch43, and maybe even used scope.$watchCollection44. The $$watchers property has all of the watchers on a scope.

Watchers are the single most important aspect of an AngularJS application’s data-binding capabilities, but AngularJS needs our help in order to trigger those watchers; otherwise, it can’t effectively update data-bound variables appropriately. Consider the following example.

<body ng-app='PonyDeli'>
   <ul ng-controller='foodCtrl'>
      <li ng-bind='prop'></li>
      <li ng-bind='dependency'></li>
   </ul>
</body>

angular.module('PonyDeli').controller('foodCtrl', function ($scope) {
   $scope.prop = 'initial value';
   $scope.dependency = 'nothing yet!';

   $scope.$watch('prop', function (value) {
      $scope.dependency = 'prop is "' + value + '"! such amaze';
   });

   setTimeout(function () {
      $scope.prop = 'something else';
   }, 1000);
});

So, we have 'initial value', and we’d expect the second HTML line to change to 'prop is "something else"! such amaze' after a second, right? Even more interesting, you’d at the very least expect the first line to change to 'something else'! Why doesn’t it? That’s not a watcher… or is it?

Actually, a lot of what you do in the HTML markup ends up creating a watcher. In this case, each ng-bind directive created a watcher48 on the property. It will update the HTML of the <li> whenever prop and dependency change, similar to how our watch will change the property itself.

This way, you can now think of your code as having three watches, one for each ng-bind directive and the one in the controller. How is AngularJS supposed to know that the property is updated after the timeout? You could remind AngularJS of an update to the property by adding a manual digest to the timeout callback.

setTimeout(function () {
   $scope.prop = 'something else';
   $scope.$digest();
}, 1000);

I’ve set up a CodePen without the $digest49, and one that does $digest50 after the timeout. The more AngularJS way51 to do it, however, would be to use the $timeout service52 instead of setTimeout. It provides some error-handling, and it executes $apply().

$timeout(function () {
   $scope.prop = 'something else';
}, 1000);
  • $apply(expr)53
    parses and evaluates an expression and then executes the $digest loop on $rootScope

In addition to executing the digest on every scope, $apply provides error-handling functionality as well. If you’re trying to tune performance, then using $digest might be warranted, but I’d stay away from it until you feel really comfortable with how AngularJS works internally. One would actually need to call $digest() manually very few times; $apply is almost always a better choice.

We’re back to the second question now.

  • What triggers a $digest?!

Digests are triggered internally in strategic places all over AngularJS’ code base. They are triggered either directly or by calls to $apply(), like we’ve observed in the $timeout service. Most directives, both those found in AngularJS’ core and those out in the wild, trigger digests. Digests fire your watchers, and watchers update your UI. That’s the basic idea anyway.

You will find a pretty good resource with best practices in the AngularJS wiki, which is linked to at the bottom of this article.

I’ve explained how watches and the $digest loop interact with each other. Below, I’ve listed properties related to the $digest loop that you can find on a scope. These help you to parse text expressions through AngularJS’ compiler or to execute pieces of code at different points in the digest cycle.

Phew! That’s it. It wasn’t that bad, was it?

The Scope Is Dead! Long Live the Scope! Link

Here are the last few, rather dull-looking, properties in a scope. They deal with the scope’s life cycle and are mostly used for internal purposes, although you may want to $new scopes by yourself in some cases.

  • $$isolateBindings59
    isolate scope bindings (for example, { options: '@megaOptions' } — very internal
  • $new(isolate)60
    creates a child scope or an isolate scope that won’t inherit from its parent
  • $destroy61
    removes the scope from the scope chain; scope and children won’t receive events, and watches won’t fire anymore
  • $$destroyed62
    has the scope been destroyed?

Isolate scopes? What is this madness? The second part of this article is dedicated to directives, and it covers isolate scopes, transclusion, linking functions, compilers, directive controllers and more. Wait for it!

Further Reading Link

Here are some additional resources you can read to extend your comprehension of AngularJS.

Please comment on any issues regarding this article, so that everyone can benefit from your feedback.

(ml, al)

Footnotes Link

  1. 1 http://ponyfoo.com/articles/getting-over-jquery
  2. 2 https://github.com/angular/angular.js/wiki/Understanding-Scopes
  3. 3 https://github.com/angular/angular.js/wiki/Understanding-Scopes
  4. 4
  5. 5 https://github.com/angular/angular.js/tree/v1.3.0
  6. 6 http://en.wikipedia.org/wiki/Scope_(computer_science)#Block_scope
  7. 7 http://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping
  8. 8 http://ponyfoo.com/articles/where-does-this-keyword-come-from
  9. 9 http://ponyfoo.com/articles/where-does-this-keyword-come-from
  10. 10 https://developers.google.com/chrome-developer-tools/docs/commandline-api#0_-_4
  11. 11 http://jquery.com/
  12. 12 https://github.com/angular/angular.js/blob/caed2dfe4feeac5d19ecea2dbb1456b7fde21e6d/src/jqLite.js
  13. 13 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L127
  14. 14 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L131
  15. 15 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L217
  16. 16 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L223
  17. 17 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L221
  18. 18 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L218
  19. 19 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L220
  20. 20 http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern
  21. 21 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L1092
  22. 22 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L1089-L1109
  23. 23 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L1134-L1182
  24. 24 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L1206-L1258
  25. 25 https://mixpanel.com/
  26. 26 https://github.com/angular/angular.js/blob/v1.3.0/src/Angular.js#L1293
  27. 27 http://codepen.io/bevacqua/pen/qmBGd
  28. 28 http://codepen.io/bevacqua/pen/CzGla
  29. 29 http://codepen.io/bevacqua/pen/HsBCa
  30. 30 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L710
  31. 31 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L1271
  32. 32 http://docs.angularjs.org/api/ng.$rootScope.Scope#methods_$digest
  33. 33 http://docs.angularjs.org/api/ng.$rootScope.Scope#methods_$watch
  34. 34 http://docs.angularjs.org/api/ng.$rootScope.Scope#methods_$watch
  35. 35 http://docs.angularjs.org/api/ng.$rootScope.Scope#methods_$digest
  36. 36 http://docs.angularjs.org/api/ng.$rootScope.Scope#methods_$watch
  37. 37 http://docs.angularjs.org/api/ng.$rootScope.Scope#methods_$digest
  38. 38 http://docs.angularjs.org/api/ng.directive:ngController
  39. 39 http://docs.angularjs.org/api/ng.$compileProvider#methods_directive
  40. 40 http://docs.angularjs.org/api/ng.$rootScope.Scope#methods_$apply
  41. 41 http://docs.angularjs.org/api/ng.$compileProvider#methods_directive
  42. 42 http://docs.angularjs.org/api/ng.$rootScope.Scope#methods_$digest
  43. 43 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L356
  44. 44 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L530
  45. 45 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L356
  46. 46 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L530
  47. 47 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L383
  48. 48 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/directive/ngBind.js#L54-L68
  49. 49 http://codepen.io/bevacqua/pen/lLbtI
  50. 50 http://codepen.io/bevacqua/pen/vwDoz
  51. 51 http://ponyfoo.com/articles/the-angular-way
  52. 52 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/timeout.js#L35
  53. 53 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L1018-L1033
  54. 54 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L922-L924
  55. 55 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L955-L967
  56. 56 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L736-L744
  57. 57 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L969-L971
  58. 58 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L970
  59. 59 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/compile.js#L756
  60. 60 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L193
  61. 61 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L857
  62. 62 https://github.com/angular/angular.js/blob/v1.3.0/src/ng/rootScope.js#L863
  63. 63 http://ponyfoo.com/articles/the-angular-way
  64. 64 https://github.com/angular/angular.js/wiki/Anti-Patterns
  65. 65 https://github.com/angular/angular.js/wiki/Best-Practices
  66. 66 http://todomvc.com/examples/angularjs/#/
  67. 67 https://egghead.io/
  68. 68 http://www.ng-newsletter.com/
SmashingConf New York

Hold on, Tiger! Thank you for reading the article. Did you know that we also publish printed books and run friendly conferences – crafted for pros like you? Like SmashingConf Barcelona, on October 25–26, with smart design patterns and front-end techniques.

↑ Back to top Tweet itShare on Facebook

Nico is an enthusiastic JavaScript hacker based in Buenos Aires, Argentina. When he's not hacking away at freelance projects, or contributing to the open source community as @bevacqua, Nico spends time writing content for Pony Foo and devoting time to local communities such as NodeSchool and NodeBots as well as Beer.js; and he’s published a book, named JavaScript Application Design.

  1. 1

    Diego Navarro

    January 22, 2015 6:01 pm

    Nice article, a very good definitions of variables and $digest behavior. Would be great to use more graphics to explain life-cycles on Angular

    2
  2. 2

    Using the root scope and emit() for pub/sub is a great idea. I use root scope and broadcast in a notification service to make sure no scopes get missed but it’s really chatty since every notification hits every scope regardless. A better approach would would be channels with emit and maybe a tellEveryone() using broadcast for a global notify. Thanks!

    0
  3. 3

    Jimmy Breck-McKye

    January 22, 2015 8:56 pm

    Is this article perhaps a year too late?

    My sense is that Angular has passed its peak. Between the 2.0 controversy, the decision to only support evergreen browsers in 1.4, and the rise of solid alternatives (Ember, React), I’m not sure if learning about the internals of Angular 1.x is really a great investment right now.

    -6
    • 4

      Ember was around before Angular so it’s hardly a replacement and React is the new kid on the block so early adopters love it but Angular is just hitting it’s stride and plenty of people are still using it for the first time so this is a very timely article for those people, along with people like me who have been using it for a year or so but still need to understand the internals more.

      The 2.0 controversy means that Angular 1.x is going to be relevant fora long time to come yet.

      13
      • 5

        It’s backed by google, so yeah, it is still relevant and it will be in the foreseeable future.

        -8
        • 6

          Jimmy Breck-McKye

          January 23, 2015 12:56 am

          The fact it’s ‘backed by Google’ doesn’t mean much to me. Google have a very bad habit of leaving developers reliant on their technologies and APIs in the cold, and the lack of an obvious migration path from NG1.x to 2.0 doesn’t suggest – to me – that Google has prioritized those teams and developers who are already invested in the original technology.

          Whilst the Angular team have, to some uncertain degree of formality, promised “between eighteen months and two years’ worth of support” to 1.x once 2 comes out, it’s going to be a hard sell to build new projects in a frozen technology come the end of this year. You’re locked out of further enhancements, improvements in design, and you’re going to find it harder and harder to hire people and find support over time.

          I suppose it depends what sort of work you’re doing and the kind of lifespan you’re foreseeing. My perspective is from someone choosing a technology for a large organization to use over the next three or four years, mostly catering to developers who don’t have a lot of front end experience. For that, I can see a lot of problems with NG1.3. But if I were creating a campaign site, for example, or working at an agency, I’d have a different perspective – I’d choose Angular because at the moment, it seems to be the best of its breed in terms of features, community and tooling.

          4
          • 7

            Taylor McIntyre

            February 25, 2015 7:36 pm

            2.0 is more of a syntactic upgrade to support the upcoming web standards (ecma6, web components, etc.) The angular team introduced many proprietary (to angular) technologies in 1.x — many of these proprietary technologies are being standardized in ecma6 (take modules for example), so naturally the angular team is doing away with their solution in favor of the standardized one. If you read through the 2.0 design docs, you’ll find that not much has changed from a conceptual point of view — and what is changing probably should change… and many experienced angular developers are already practicing the changes in 1.x (for example, using bindToController in directives). I don’t think Google is concerned with making the best Angular JS, they’re concerned with making the best possible web framework.

            0
    • 8

      Above what has been said – Angular just not long ago stepped into its enterprise maturity stage – check large scale businesses using it and amount of relevant job positions). It’s most productive, widespread and long term part of technology Hype Cycle. So diving into details under the hood today for those who are already advanced users is very up to date.

      1
    • 9

      Total Farking BS. If you and your team can’t handle learning something new then just stick with jsp or something.

      -7
    • 10

      ** the decision to only support evergreen browsers in 1.4 **

      do you have a link for that? I’ve looked around and haven’t seen anything definitive. I did see a planning doc that referenced evergreen browsers and IE9+ for 1.4. Just looking for some clarity.

      0
  4. 11

    Angular isn’t going anywhere anytime soon. The 2.0 will be different for sure, but the old versions will have support after that for some time aswell. It is interesting though, what the situation will be when the 2.0 arrives.

    0
    • 12

      Jimmy Breck-McKye

      January 23, 2015 12:44 am

      It’s true enough that the release of Angular 2.0 isn’t going to stop Angular 1.x apps from working.

      But what it does mean is that any new projects are going to face a quandary within the next few years, when it becomes harder to find developers, documentation and support. It also means asking new developers to go through the rather steep learning curve that 1.x exhibits for the sake of getting to grips with an obsolete technology. This is a difficult sell. Furthermore, it also makes the chances of using and exploiting new technology as part of an existing app a lot worse – there’s no continuous development of integration with, for example, new templating languages, new language primitives, or new web technologies.

      I would need to be convinced of the wisdom in starting a new project in Angular 1.x today. Unless it was something quite small that wouldn’t ever be updated, or is going to be thrown away anyhow (e.g. a campaign site).

      3
  5. 13

    Great article on a very hard subject to truly digest, no pun intended. Keep up the good work Nicolas. I will follow in PonyFoo for more great stuff. Thanks.

    4
  6. 14

    Very nice article…..liked it a lot….thanks…waiting 4 the next article.

    2
  7. 16

    Nice article. You clearly have a thorough understanding of the entire idea of scoping! I am missing the AngularJS team’s recent opinion on scopes, though.

    Even if you are not planning on moving to AngularJS 2.x any time soon, the AngularJS team’s point on scopes being ‘bad’ should be addressed in current code as well. In the current 1.2+ branches (with controllerAs) the use of scope can be pretty much eliminated, which imho is a good thing for testing and reliability purposes. I would (at least) like to see your opinion on this matter ;) …

    At our company, we are trying to eliminate isolated scopes as well, hopefully helping us make the migration path from AngularJS 1.x to 2.0 less bothersome in the future. We are using shared services for communication between controllers and (related) directives, which we postfix with ‘Communicator’ to set them aside from ‘normal’ services. If you are interested in any details, I am more than happy to provide some code ;) …

    Happy coding!

    3
  8. 17

    Great article!

    0
  9. 18

    So, I want to know more opinions about Angular 1.x

    We are now moving all our front end into an Angular project. The team is right now learning it insights.

    I wonder whereas this is a good movement or a really bad one.

    I guess somebody will fork 1.x and keep going from there. Maybe google re-think it support.

    But, it will be for sure a 2 year maximum project ?

    How hard will be the migrations plus new concepts for the team ?

    Any advice ?

    We still on the middle, maybe to change from Angular to another one might still being a possibility.

    Thanks !

    0
    • 19

      Interesting question as currently at my work we also are starting enterprise class project with angular and I’m also trying to find out if we did right decision. What could be other option?

      0
  10. 20

    Great article – love the examples of using scope for pub/sub.

    By the way, on your code samples, the string literal ‘food.order’ needs the closing apostrophe (in both samples).

    2
  11. 21

    I’d like to argue about using $rootScope.$on, like it’s said here:
    “If you have two components that communicate through $rootScope, then you might prefer to use $rootScope.$emit (rather than $broadcast) and $rootScope.$on.”
    The issue happens, when you use subscribing to $rootScope events directly (instead of $scope.$on) in things that can be reinitialized (like controllers and directives).
    When you subscribe using $rootScope.$on in controller and this controller is instantiated with ng-if or with $route, and this controller is destroyed, the event handler remains and consumes memory. After this controller is instatiated again, new handler subscribes to the same event, after that you have classical side-effects of double subscription to one event. When instantiated for the third time, it subscribes three times to one event, and so on.
    The same is true for directives, too.
    So, using this technique, you’ll make your application fragile and will run into memory leaks issues.
    There is a way to fix this: in your controller you shold listen to “$destroy” event, and unsubscribe your $rootScope’s event listener. But one developer could forget about it, and bugs like this are not so easy to find. So, the best practice here, as far as I see it, is to avoid using $rootScope.$on in things that can be reinitialized.

    4
  12. 22

    Very nice article!

    1
  13. 23

    I’d really like to read the second part of this article on directives. When will it be published?

    0
  14. 24

    Thank you,
    its really worth for my learning.

    0
  15. 25

    Binh Thanh Nguyen

    June 28, 2015 11:05 am

    Thanks, nice post

    -1
  16. 26

    Great post! I’m definitely interested in reading the second part of this article. When is it coming?

    1

↑ Back to top