codelord.net

Code, Angular, iOS and more by Aviv Ben-Yosef

Angular 2 Preparation: Killing Controllers

| Comments

Controllers are dying. The migration path doesn’t even make a reference of them. Once controllers were a cornerstone of Angular. Now we’re all trying to sweep them under the carpet.

The question that bothers me is what can you do today to make your life easier once Angular 2 is out. Last time I discussed a first step – making controllers smaller and cleaner.

But I’ve been toying with not writinga controllers at all. I’ve seen several people already do this and my experience so far is nice. We’re all still learning this together. I figured I’d share how I made it work for me.

This preparation step will, hopefully, make your transition to Angular 2 easier and smoother. Especially once ng-upgrade is out and you’ll be able to use all your code units.

The mechanism: component directives

We achieve controller annihilation by using directives everywhere you’d use a controller. That means your app’s code is now either in a directive or in a factory (service).

It’s important to make a mind shift. Stop thinking about directives as a building block for reusable code. It’s a building block, period.

Isolated directives are self-contained components that I find easy to reason about and maintain.

I prefer to use directives with a controller function and not use link. That’s mostly because controller means I almost never need to inject $scope. Also, because using controllers means I can then require them in child directives.

A basic example: controller turned directive

Here’s a very basic controller that we’ll turn into a directive (plunk):

1
2
3
4
5
6
7
8
9
module.controller('MessagesCtrl', function() {
  var self = this;
  self.list = [
    {text: 'Hello, World!'}
  ];
  self.clear = function() {
    self.list = [];
  };
});

And its template is:

1
2
3
4
5
6
7
8
<div ng-controller="MessagesCtrl as messages">
  <ul>
    <li ng-repeat="message in messages.list">
      {{message.text}}
    </li>
  </ul>
  <button ng-click="messages.clear()">Clear</button>
</div>

(Did I mention it was basic?)

Here’s the after picture (plunk):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.directive('messages', function() {
  function MessagesCtrl() {
    var self = this;
    self.list = [
      {text: 'Hello, World!'}
    ];
    self.clear = function() {
      self.list = [];
    };
  }
  return {
    templateUrl: '', // Same as for controller
    scope: {}, // Isolate == Awesome
    controller: MessagesCtrl,
    controllerAs: 'messages',
    bindToController: true
  };
});

What have we got here?

  • Notice that the template didn’t need any changes.
  • Same goes for the actual controller function itself.
  • We made sure to define the directive with controller:, controllerAs:, bindToController: and isolated scope:. Just the right incantation.

This resulted in a little more boilerplate but we got rid of the controller. Along the way we also earned an isolated scope, win! And, we have a true component. You can take a look at this .js file and you see all you need to know – the inputs, the template, the controller.

Interesting notes and caveats

Do DOM manipulation in the directive controller: This might feel wrong. You’ve heard the mantra “Don’t do DOM manipulation in controllers” a thousand times. But this isn’t a controller anymore. It’s a component. Angular even let’s you inject $element inside these directives. Take use of it!

Sometimes you still need link functions: The controller function executes before the element has rendered. If, for example, you want to register an event handler on some child element you’ll have to do it in the link function. Another is when you want to use require and get access to that controller.

Defining routes: You no longer need to supply a controller in your ui-router or ng-route configuration. Just pass a simple template such as <messages></messages>.

ui-router resolves are harder: I’ve long stopped using resolve because it has so many pitfalls. But, you can get access to resolved stuff, see here.

Some widgets love controllers: If you’re using widgets such as ui-bootstrap’s modals you will see they love controllers. It’s still possible to use them without controllers. The above workaround works.

The way forward

As I said, I’m still figuring this out along with you. But so far I’ve found this to be simpler. For a long time I’ve avoided creating controllers except for routing endpoints. Now I just don’t use that as well. Having everything be a directive means less mental overload and a simpler file structure.

If you’ve been toying with this too I’d love to hear your thoughts and techniques.

“Maintaining AngularJS feels like Cobol 🤷…”

You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete. Components? Lifecycle hooks? Controllers are dead?

It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines! Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.

Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.

Get the modernization email course!

Angular 2 Preparation: Controller Code Smells

| Comments

Controllers are a big part of Angular 1 that is going away in Angular 2. As I mentioned previously, the Angular team isn’t really talking about a migration path for controllers because they’re just dead. In Angular 2 you will probably rewrite all your controllers as components – basically directives with templates.

But what should you do about your current code? You already have controllers. Should you rewrite them? Can you keep using them for the time being? Should you stop creating new ones?

I’m extremely against investing time in Angular 2 APIs/code. It’s just not there yet. But, that doesn’t mean you can’t start writing your Angular 1 code in a different mindset today.

Yes, it would be better if you’d kill all your controllers today and never write a controller again. That’s a possible way to go with – I’ve heard of several people who’ve been doing this for a while now. But if your project is large, migrating all controllers would be a pain.

That’s why I think the first major focus should be writing your controllers today so that killing them later would be easier.

To help you spot controllers that would be hard to kill, here’s a list of controller code smells for Angular 2 preppers:

Not using controller-as syntax

Using controller-as syntax has been part of our community’s guidelines for a while now. Using it makes for more maintainable controllers and less scope soup.

In the Angular 2 perspective, it also means that you are taking the first step towards creating clear boundaries. In your templates it should always be clear what scope/controller. Using this form will make it easier to tell exactly where a controller is being used. It will make a future migration more of a simple task and not feel like an explorer fighting unknown references.

Injecting $http to a controller

This is actually just a very common indicator of a controller doing too much work. Controllers should be thin when it comes to logic. You should push essentially anything that doesn’t relate directly to manipulating the view model to a factory.

Putting lots of stuff on $scope

Exposing a lot of logic to the template (even not using $scope, but the controller itself, in the case of controller-as) is another smell of a controller biting off too much.

Thin your controllers so that they are small and do one thing well. If necessary extract out sub-directives and sub-controllers. Just don’t let a single file have hundreds of lines. That’s a recipe for a file you’ll have a hard time converting. Also, these files tend to just grow larger – once you’ve passed critical mass there’s no stopping them – so coder beware.

Accessing $scope.$parent

One of my best tricks for maintainable code is having clear boundaries and decoupling. That’s why I almost always prefer writing my directives with isolated scope.

Seeing a controller peeking into its parent’s scope makes me cringe. That’s a definite recipe for bugs and a migration nightmare.s

Using $rootScope

Similar to the previous point, $rootScope is another way of entangling pieces of code, making them harder to change later.

Sometimes it’s the right tool, but you should always be wary of introducing more globally shared state.

Injecting a lot of things

Again, your controllers should be as thin as possible. If said controller is injecting 10 services, I find it hard to believe that it’s thin enough. Dumb it down, extract logic to other parts and make sure your controllers are in small bite-sized chunks that you can easily reason about and test later on when you might want to get rid of them.


The road to Angular 2 is still a way ahead, but simple steps like these will make it easier once we get there. In a future post we’ll see how you can stop writing controllers (or, at least, less of them).

“Maintaining AngularJS feels like Cobol 🤷…”

You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete. Components? Lifecycle hooks? Controllers are dead?

It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines! Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.

Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.

Get the modernization email course!

$q.defer: You’re doing it wrong

| Comments

Using promises is tricky at first. Every newcomer to Angular will be flustered about them. It’s super easy to get lost at first between all the different nitpicks: promise chaining, $http’s special promises, the asynchronous nature of it all, etc.

One of the first questions you tackle starting to learn promises is when, if at all, should you use $q.defer()?

I used $q.defer() quite often when I first started with Angular. You know how much of that original code actually needed to use defer()? About none at all.

I realized that there was almost no reason to use defer() to create my own promises. It’s just cumbersome and error prone.

And guess what? I think that nearly everyone that uses it does it unnecessarily.

Real examples from GitHub

First, a disclaimer: this isn’t some sort of shaming. I used to write lots of code just like this. I want to show how common this problem is with real code and no handwaving.

I opened GitHub’s code search and started looking for people using $q.defer(). In the first 3 pages of results I saw about 2 actual valid use cases. Yeeeep.

You don’t need defer to create a simple-valued promise

Let’s take a look at this case:

1
2
3
4
var defer;
defer = $q.defer();
defer.resolve(['detail', 'simple']);
return defer.promise;

It’s easy to see the author just wanted to create a promise with some hard-coded value. Those 4 lines? They can just be rewritten as:

1
return $q.when(['detail', 'simple']);

Same goes for this longer case or that one. $q.when() is perfect for when you want to turn a simple value into a promise.

You don’t need defer to change the value of a promise

A lot of coders like changing $http’s special promises to work like regular ones – and rightly so. Take a look at this way of doing it:

1
2
3
4
5
var defer = $q.defer();
$http.get('options.json').success(function(result) {
  defer.resolve(result);
});
return defer.promise;

This successfully creates a promise that you can call with .then() and receive as a value the actual response body. But, through the magic of promise chaining you can simplify this:

1
2
3
return $http.get('options.json').then(function(response) {
  return response.data;
});

Promise chaining is a bit complicated, but the gist is this: inside a promise’s .then() whatever you return will be passed along to the next .then() along the chain.

This means that in our example above we’re returning a regular promise whose .then() will pass response.datasweet!

Sometimes you don’t need to do anything at all

Just about anything asynchronous is already a promise in Angular. Take a look at this:

1
2
3
4
5
var defer = $q.defer();
$timeout(function() {
  defer.resolve();
}, 5000);
return defer.promise;

The author wanted to return a promise that would get resolved once 5 seconds have passed. But guess what? $timeout already returns a promise that does just that, allowing us to write this:

1
return $timeout(function() {}, 5000);

When should you use defer?

Basically, the main reason is:

You’re converting a callback API to be promise-based: For example, if you wrap a jQuery plugin to work in Angular. Callbacks are not as fun as promises. To kickstart the promise chain and convert callbacks you have the create the first promise.

I’ve written about actually creating promises here.

“Maintaining AngularJS feels like Cobol 🤷…”

You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete. Components? Lifecycle hooks? Controllers are dead?

It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines! Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.

Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.

Get the modernization email course!

When Does a Directive’s Link Function Get Called Again?

| Comments

If you’ve working with Angular for a while surely you’ve seen a directive’s link function being called more than once. I know that when I started it used to catch me off guard. I keep seeing developers puzzled, asking “why is it being re-run?”

While it is being executed several times (e.g. if you add a console.log() statement you see it print multiple lines) – it is not technically being rerun.

The link function is like a directive’s constructor. It is executed only once during the initialization process of the directive. There’s no way to get the link function to run again for the same instance of an already initialized directive.

If that’s the case, though, why are you seeing it run multiple times?

Well, it is not rerunning. It’s running as part of the initialization of a whole new directive. It means that you are destroying and recreating your directives, and whenever a directive is being initialized we’ll see its link function run.

Why are your directives being recreated?

There are several possible reasons, but the most common ones are:

  • using ng-if which can destroy the element and recreate it (unlike ng-show).
  • Using ng-repeat where each item contains a directive. If you don’t set it up correctly ng-repeat can be a bit wasteful with recreating elements.

Is this a problem?

Not necessarily. Make sure your code doesn’t assume that a certain directive will be created only once.

This might become a problem if those initializations are on the heavy side and having them run multiple times starts causing a performance issue. Just be on the look out for your app getting slow and you should be all right.

“Maintaining AngularJS feels like Cobol 🤷…”

You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete. Components? Lifecycle hooks? Controllers are dead?

It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines! Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.

Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.

Get the modernization email course!

Angular 2 Migration Path: What We Know

| Comments

The main concern among us Angular developers recently has been the future of our existing Angular 1 apps. The community was abuzz with fears of apps they won’t be able to upgrade:

“Will it be possible?”
“Will it require a ton of work?”
“Will my boss never forgive me for making us write a huge app in a dead framework that we’d be stuck with?”

In their latest post the core team shared some very positive changes.

tl;dr – Seeing the changes in the migration path makes me feel a lot more comfortable. It eliminates most of my fears about my existing Angular 1 projects and their future. w00t!

What’s Newly Announced

Essentially, we’ll have nearly complete interoperability between Angular 1 and 2. I don’t know about you, but when I first read this I was literally going “YES” and did a fist pump.

The core team is working on ng-upgrade, a library that enables calling Angular 1 code from Angular 2 and vice versa.

Directives and Services FTW

We will be able to migrate our directives and services one-by-one to Angular 2. These will still be able to inject Angular 1 services and use other Angular 1 directives. Also, code that you haven’t migrated yet will still be able to use them. Really, a dream.

Baby Steps == Bigger Wins

I’m really pumped about this because it means we will really be able to migrate one little part at a time. Migrating whole routes – which was the previous suggested migration path – would have been really time consuming and tricky.

Reducing Fears About Dependencies

A big thing we were worried about is getting stuck until all 3rd party code will be migrated as well.

These changes mean most libraries we use should still operate nicely even if they haven’t been officially migrated to Angular 2. Sweet!

What remains to be seen

No real code examples: The ng-upgrade repository has 0 lines of code. Angular’s 1.5 milestone is currently dubbed migration-facilitation, so let’s cross our fingers and hope to see all this goodness come to life soon.

What about controllers: I’ve haven’t mentioned controllers anywhere in this post. We’ve known for a while that controllers are dead in Angular 2. We will probably have to convert them by hand. This is one more reason you should get used to using the controller-as syntax, pushing as much logic as possible out of your controllers and probably just using controllers less.

ES5, ES6 and TypeScript: The core team highly recommends doing Angular 2 in TypeScript. How hard will the conversion be? What will happen to those of us that decide to stay with ES5/6? They say all options will work, but we haven’t seen a lot of love for these setups yet.

Should you start using Angular 2?

My answer remains the same as it was a couple of months ago: Not yet. You should start making sure your app is in shape for easier migration. More on that in the upcoming posts.

“Maintaining AngularJS feels like Cobol 🤷…”

You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete. Components? Lifecycle hooks? Controllers are dead?

It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines! Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.

Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.

Get the modernization email course!

Controller-Directive Communication Part 3: Controller to Directive

| Comments

This is the last part of my controller and directive communication series. In the first part we looked at setting up simple bindings. The second part showed the use of bound functions to let the directive send messages up to the controller.

This part focuses on the remaining combination: having the controller send messages down to the directive.

Controller to Directive Communication

Continuing with the chat app example from the previous parts, let’s now assume that our controller sometimes wants to tell the chat list to scroll to the bottom (e.g. because the user marked the chat as read from another device).

This means that now our controller needs to somehow tell the directive to do something. We want the directive to expose something to us. Directives can receive functions via bindings, but there’s no straightforward way for them to create new bound functions and expose them up the chain.

The Suboptimal Options

There are a few possible techniques that are somewhat popular/recommended at some places. I’m listing them here mainly to say why I dislike them – scroll down to my preferred solution if you’re in a hurry.

Events? I’d Rather Not

A lot of developers would just shrug and use events (Using $scope.$broadcast, $scope.$on and $scope.$emit).

While this will work, I find events to be catalysts for code quality deterioration.

Using events means you have to dig through a component’s code to find which events it is firing or watching for. There’s no easy-to-find API to understand the dependencies between components.

Services? Not really

Services are the go-to for communication between entities in many Angular scenarios, and they rightly are. But this scenario feels different.

This isn’t a a concern that’s relevant across the whole app. It’s internal communication between two instances on screen, that would feel wrong for me to put in a service.

I prefer to use services for things that are global – cross cutting concerns that are synced across the app, and not a component and its inner component trying to nudge a pixel.

Scope Mangling

I don’t really have a name for this one, but I’ve seen it in the wild. It’s an abuse of binding to slap on things on objects willy-nilly.

Basically, our controller creates some object, e.g. directiveApi = {}. That object is then passed with binding to our directive. During its initialization the directive would add functions to this object (scope.directiveApi.onRealAll = function() {}).

That way the controller now has a reference to a function that was created by the directive, and can call it at will.

This feels like one big hack to me. My aesthetics always prefer the explicit solution, the one that’s least likely to shoot me in the foot.

It also introduces implicit coupling and just feels scary when you read the controller’s code – all of a sudden it makes a call to a function you’ve never seen before.

Also, it introduces a timing issue – you have to make sure not to attempt to call these functions before the directive finished its initialization. Scary.

My preferred solution – The Observer Pattern

In this case I’d rather implement my own observer pattern, even though it would require some boilerplate code, in order to avoid using events or external services.

Here’s how it goes. Our controller now manages observers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
angular.module('chat').controller('ChatCtrl', function(ChatService) {
  var chat = this;
  var handlers = [];
  chat.markAsRead = markAsRead;
  chat.onAllRead = onAllRead;
  activate();

  function activate() { /* Setup */ }

  function markAsRead() {
    ChatService.markAsRead();
    angular.forEach(handlers, function(handler) {
      handler();
    });
  }

  function onAllRead(handler) {
    handlers.push(handler);
  }
});

And the directive looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
angular.module('chat').directive('chatMessageList', function() {
  return {
    scope: {
      list: '=chatMessageList',
      onAllRead: '&' // The registration hook
    },
    template: 'something', // See full plunker
    link: function(scope, element) {
      var $list = $(element).find('.js-chat-list');
      scope.onAllRead({
        handler: function() { scrollToBottom($list); }
      });
    }
  };
});

Full Plunker Example

We’re using the isolated scope in order to pass the directive a function, onAllRead, but this time that function is used by the directive to register for change notifications on the controller.

ChatCtrl saves the directive’s handler and will make sure to call it whenever it needs to notify someone about this.

This requires more typing, but I prefer it since our code is now very clear. It’s really easy to understand the flow even if you are unfamiliar with the codebase and stumbled upon any one of our source files by its own.

It is also more robust. If we ever move things around and forget to update all the sources it would break at a useful point – when the directive would try to register to a function that is no longer there. In other scenarios, like scope mangling, it would break when the controller would try to send the notification to no one.

As a rule of thumb, this is the way I’d go with most of the time unless I have a pretty good reason to use something else.

Note: When passing functions by yourself to other places in Angular, like we do in this case, you should make sure those functions are no longer kept anywhere once the scope they’re bound to is destroyed. Failing to do that would mean memory leaks and undefined behavior. Check out the full plunker example to see how exactly this is done.

That’s it. I’m more than interested in feedback about this, feel free to contact me. The important takeaway is that Angular is not trivial for communication between parts, but that doesn’t mean you shouldn’t be breaking your system to small, maintainable chunks.

“Maintaining AngularJS feels like Cobol 🤷…”

You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete. Components? Lifecycle hooks? Controllers are dead?

It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines! Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.

Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.

Get the modernization email course!

Controller-Directive Communication: Part 2

| Comments

In the previous part we saw the simple case of a component, represented by a directive, that we use in our controllers in a simple set and forget setup: the component just needs some input and can do its job using that alone.

In those cases, using the simple magic of data-binding is really all you need. But once you go around the block a couple of times you’ll eventually come upon the need to write a smarter component.

This part will focus on a directive talking with its parent controller. That means we have a directive that would like to tell its parent that something has happened. Note that in cases of directive-to-parent-directive communication the best practice might be something else.

Directive to Controller Communication

Let’s say that we’re going to change the behavior of our chat app a bit. We don’t want the view to scroll to the newest message automatically now – we got plenty of users complaining about losing track of their place in conversations that way.

Instead, there’s no auto scrolling and we’ll want the chat list directive to let the controller know whenever the user scrolled to the bottom of the list, so the controller would know to mark that chat as read (e.g. in order to send a read receipt to the other party).

First, we add this function to our ChatCtrl (you can see the previous post’s code here):

1
2
3
chat.readMessages = function(lastMessageRead) {
  ChatService.markAsRead(lastMessageRead);
};

We pass it to the directive by adding read-messages="chat.readMessages(lastMessageRead)" in our template.

And we change our directive like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
angular.module('chat').directive('chatMessageList', function() {
  return {
    scope: {
      list: '=chatMessageList',
      readMessages: '&' // <-- This is new!
    },
    template: '<div class="chat-list js-chat-list">' +
                '<div ng-repeat="message in list">' +
                  '{{message.text}}' +
                '</div>' +
              '</div>',
    link: function(scope, element) {
      var $list = $(element).find('.js-chat-list');
      $list.on('scroll', function() {
        if (isScrolledToBottom($list)) {
          scope.$apply(function() {
            var lastMessage = scope.list[scope.list.length - 1];
            scope.readMessages({lastMessageRead: lastMessage); // <-- This too
          });
        }
      });
    }
  };
});

Full Plunker Example

As you can see, in this scenario we needed the directive to notify its parent controller of something. That was pretty easy to achieve – we used the isolated scope to pass along a callback function readMessages() that the directive can call whenever it needs to.

There are a few alternatives, like using events, but I’m pretty certain this is the way most Angular developers would go with for a scenario like this. It’s easy, clear and readable.

Passing Function Bindings

As you may have noticed, passing a function binding doesn’t look likely simply binding on some data. When we passed the chat list we specified it on the isolated scope as list: '=chatMessageList'. That equals sign means it’s a “simple” data binding – Angular keeps evaluating the expression that the parent controller passes the directive and if it notices a change it syncs it to the directive’s scope.

You can imagine that Angular just regularly checks the value of chat.messages in the parent controller and whenever the expression returns a different value (e.g. because we’ve put a different instance of a list there) it does something along the lines of directiveScope.list = controllerScope.chat.messages in order to bind them together.

Functions are different. With functions we don’t want Angular to regularly evaluate readMessages(lastMessageRead) – it doesn’t make sense. We want that function to run only on specific times, not on every digest cycle. Also, lastMessageRead doesn’t exist anywhere, it’s the name of an argument.

That’s why when we pass function bindings we have to use a different syntax when defining them in the scope, in this case readMessages: '&' – that ampersand is the way of saying this is a function binding.

Another detail is that we don’t call these functions like we define them. The original readMessages() function receives a single argument, so you might expect us to call it as scope.readMessages(message) – but that’s not how it’s done.

The syntax is a bit different, where we have to specify arguments to the functions inside a single object where the key names are the names of the arguments.

So scope.readMessages(message) turns into scope.readMessages({lastMessageRead: message}).

While you might find it odd the first couple of times, it’s really not that big of a deal and grows on you fast.

Function bindings are a very important building block in Angular in general and specifically in creating robust and reusable directives. Making it possible to create simple callbacks allows for a lot of flexibility.

The Way Forward

In the next part we’ll see how function bindings can be used to allow the controller to call the directive and notify it of changes. Make sure to get it by subscribing to my mailing list below.

“Maintaining AngularJS feels like Cobol 🤷…”

You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete. Components? Lifecycle hooks? Controllers are dead?

It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines! Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.

Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.

Get the modernization email course!

Controller-Directive Communication: Scroll to Bottom

| Comments

A common question in Angular is how should two components talk to each other. The flexibility of Angular means you have a lot of choices to make, and there’s little guidance.

The way you’d have two sibling controllers communicate is different from how a controller will talk to its parent controller, how a directive will talk to its parent directive and how controllers and directives talk with one another.

In this part we’ll go over a common use-case, scroll to bottom, in order to show the straightforward way of communicating between controllers and directives: Set and forget.

The next 2 parts will be more in-depth about the communication itself in the scenarios of directive to controller communication and controller to directive communication.

Set and Forget + Scroll to Bottom Example

Say we have a simple chat client controller that is made of a list of messages and at the bottom an input to enter new messages (basically any text/messaging app you’ve used).

We’d like the message list to scroll to the bottom whenever a new message is added, either by the user or when a new message is received from the server.

Of course DOM manipulation shouldn’t happen in the controller, so we’ll whip up a nice little directive for it. Our screen should look roughly like this:

And our simple controller code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
angular.module('chat').controller('ChatCtrl', function(ChatService) {
  var chat = this;
  chat.addMessage = addMessage;
  activate();

  function activate() {
    ChatService.getMessages().then(function(messages) {
      chat.messages = messages;
    });
  }

  function addMessage(message) {
    chat.messages.push({text: message});
    ChatService.addMessage({text: message});
  }
});
1
2
3
4
5
6
7
<div ng-controller="ChatCtrl as chat">
  <div chat-message-list="chat.messages"></div>
  <form ng-submit="chat.addMessage(chat.newMessage)">
    <input ng-model="chat.newMessage">
    <button type="submit">Send</button>
  </form>
</div>

We have a very basic ChatCtrl that uses a chat list directive. That directive is being passed our list of messages, and it is responsible for displaying that list on screen.

Here’s how we’d implement that directive:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
angular.module('chat').directive('chatMessageList', function() {
  return {
    scope: {
      list: '=chatMessageList'
    },

    template: '<div class="js-chat-list">' +
                '<div ng-repeat="message in list">' +
                  '{{message.text}}' +
                '</div>' +
              '</div>',

    link: function(scope, element) {
      scope.$watchCollection('list', function() {
        var $list = $(element).find('.js-chat-list');
        var scrollHeight = $list.prop('scrollHeight');
        $list.animate({scrollTop: scrollHeight}, 500);
      });
    }
  };
});

Full Plunker Example

The Actual Communication

As you can see our directive has an isolated scope: in the directive definition we provide a value for scope and define the different attributes of the scope and how they should be bound.

Using an isolated scope makes it really easy to pass specific “arguments” to our scope and having those arguments be easily bound between the controller and the directive.

Also, isolation makes for more readable and maintainable directives – you immediately see what they expect to receive as inputs in their scope definitions.

This basic use of scopes is a major building block in big Angular apps. It allows us to use directives a bit like functions where we set their input and just have it go off and do some work (e.g. “set and forget”, see what I did there?)

Scroll to Bottom Logic

This is a great use case for using $watchCollection instead of $watch.

Our directive wants to scroll to the bottom whenever a new message is added to the list of messages.

A simple $watch('list', func) would not work, because it’s a shallow watch and our list instance doesn’t change – we only add elements to it.

We could use a deep watch (i.e. $watch('list', func, true) – notice that last true) but that would be wasteful since our messages objects don’t really change – we just add things to the list. That means there’s no real reason to have Angular scan each element for changes – messages themselves are immutable.

$watchCollection is the perfect tool since it just watches the list for additions and removals and doesn’t have a deep watch for every element in it. This, as Goldilocks would say, is “just right”.

The Way Forward

The next parts will be more interesting in my opinion – how should your controller notify your directive of changes? How should the directive notify the controller? Sign up to my newsletter below to get the updates (no spam guarantee or I will spend the rest of my career only doing web in Dojo).

“Maintaining AngularJS feels like Cobol 🤷…”

You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete. Components? Lifecycle hooks? Controllers are dead?

It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines! Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.

Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.

Get the modernization email course!

Angular: Watches, Watches Everywhere

| Comments

You can only go so long in your Angular path before starting to learn about performance in Angular and some of its common problems.

You’ve probably heard about “lots of watches” being a problem. But what exactly is a “watch”? Is it just $scope.$watch()? No! Let’s dig in.

What’s in a Watch

A “watch” is Angular’s mechanism for keeping track of a specific expression and acting when it changes. Every watch has the expression that it evaluates on every digest cycle to see if it has changed and also the handler function that gets called whenever the expression’s value has changed.

Not all watches were created equal, especially from a performance perspective. By defaults watches are shallow and so pretty quick. But if, for example, you use a deep watch on a huge list, don’t be surprised if it adds a lag to your app.

The Common Types of Watches

The $watch

Yeah, the obvious case is the one I just mentioned. Whenever you explicitly use $scope.$watch('expr', function() {}) you are adding a watch for the expr expression.

Templates Binding with {{ foo }}

Whenever you use double curly brackets in a template in order to display a value you’ve set in your scope you’re basically adding a watch. This watch will keep track of the value and update the template’s DOM whenever the value changes (after all there’s no magic here – the browser doesn’t know how to bind your expression and the DOM which means Angular has to update things, it’s just hiding those details from you).

While we’re at it, let me add on a side note that just adding things to your $scope doesn’t add a watch. It’s only binding to it in some way that adds it.

Isolated Scope Bindings

When you pass to a directive with an isolated scope something on its scope, that too creates a watch.

For example, if you have a directive whose scope definition looks like this scope: {foo: '='} then Angular will make sure to keep it in sync – if the value changes in your parent scope it will also changed in the directive’s scope. This is achieved by creating a watch for foo that makes sure to update it inside the directive’s scope whenever the value changes in the parent scope.

Attributes in Templates

When you have Angular expressions used inside attributes in your templates, those are usually creating watches too.

Take ng-show and ng-hide as an example you’re probably familiar with. These directives receive an expression and do something depending on whether it evaluates to true or false. This act of tracking the expression’s value is really watching stuff over.

Filters

This is kind of a private case of the previous point, but it’s worth pointing out. Filters keep being evaluated and updated because on every digest, just like any other watch. But they are usually heavier than regular watches and do more complex things, so keep that in mind.

And, Of Course, Code

Along these watches, which are relatively straightforward to find in your code, you should also keep in mind that more watches might be used by the code of other components you may be using.

As an example, our beloved ng-repeat directive is using watches (specifically a $watchCollection) in order to update the list it received whenever changes happen. So even if you don’t see an explicit watch in your template, it doesn’t mean that it’s not there.

Watch Out

So, to sum up this post, as you can see watches are pretty much everywhere in Angular land. Next time you’re looking at the number of watches using ng-stats you should be more equipped to understand how that number came to be.

“Maintaining AngularJS feels like Cobol 🤷…”

You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete. Components? Lifecycle hooks? Controllers are dead?

It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines! Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.

Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.

Get the modernization email course!

Angular Performance Diagnosis 101

| Comments

Angular can feel magical but it is not a magic bullet. That magic comes with a cost, and that cost sometimes means slow/laggy apps.

Once you’ve noticed that some parts of your Angular app have performance problems, the hardest part is pinning down what exactly is causing the problem.

Let’s see how you might start tackling this gnarly problem:

First, take a look around your app

Before diving head first into your problem, I find it useful to take a few minutes to roam around my app to get a feel for its behavior all across. This helps realize the scope of your issues and where they are most painful.

Just go ahead and use your app a bit, checking different screens that use different widgets and modules. While doing it try to notice if you see performance differences in certain areas. Also, I find it priceless to do with exploration while having ng-stats open. It’s a super nifty code snippet that lets you easily see how long your digests are taking and the number of watchers currently in your app.

Doing this makes it easier to understand what’s your “baseline” performance – what’s the minimal number of watchers a page has, what is a high number of watchers for you, where are your digests taking too long.

Understanding your performance problem

Now, let’s try and understand what kind of performance problem exactly you’re facing.

Load/initialization vs. general slowness

Is your problem that some view takes too long to load or render? Or is it more an issue that everything feels clunky in that view, e.g. typing in inputs has some delay to it, or clicks feel like they take time to “work”?

Load time problems are, a lot of the time, not Angular specific. For example, if you’re waiting to fetch thousands of elements from the server and then list all those elements on screen, you’re gonna have things be slow sometimes even if you do it in vanilla JavaScript. Face it.

In those situations you should probably look into the general ways we’ve been treating these problems in the web for decades, like pagination (or infinite scrolling, etc.).

But, in case that the overall experience of using your app feels laggy, you might have to do some fine tuning of your Angular code to make it play well with others. Can you see slowness go with long digest cycles or areas with lots of watchers? That’s probably it.

Everywhere vs. specific screens

Is the slowness you’re seeing across the whole app or just in some specific screens, when there’s a certain widget being used, etc.?

If you can say the issue is only in part of your app you’re in good shape. It’s time to start zooming in on that slow part and inspect it to find suspects. Sometimes I just start commenting parts out till the problem goes away – it sounds stupid but a lot of times it helps me pin point the bad parts super quickly.

In cases where the slowness is all around you should look at the more general things: do you have too many things on your root scope causing a lot of watches? Might you be using some widget/plugin/foo that’s adding a lot of cost?

Some potential tricks to make things faster

A few basic tricks that you should probably know once your Angular project gets big enough:

  • If you have a big ng-repeat sometimes adding “track by” can speed things up significantly.
  • If you list is still slow you might want to look into virtualization, like using ui-grid.
  • Minimize the number of watches in general, for example replacing ng-show and ng-hide with ng-if sometimes improves performance noticeably.
  • Reduce the amount of deep watches your perform where possible – can you use regular watches, or $watchCollection etc.?
  • Using events on scopes can be a performance issue in deep scope hierarchies.
  • Reduce the number of watchers by making those watches that don’t change use the bind-once syntax.

Measure twice, optimize once

Most important is to make sure you’re not doing these changes blind. Make sure to measure the effectiveness of your optimizations. You’d be surprised how easy it is to go through a lot of hoops just to realize that you actually didn’t make performance any better (or at least not in a way that makes the optimization worth it).

The easiest way is to get a feel for your improvements by checking it out with ng-stats and similar tools, but the best would probably be to have code that repeatedly calls digest and measures the time it takes in certain scenarios. Some people even create a performance test suite that checks that things aren’t getting too slow over time. I hope to write more about this soon.

That’s it for now. May your digests be always fast!

“Maintaining AngularJS feels like Cobol 🤷…”

You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete. Components? Lifecycle hooks? Controllers are dead?

It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines! Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.

Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.

Get the modernization email course!