codelord.net

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

Angular 1.6’s Possibly unhandled rejection errors

| Comments

If you’ve updated to AngularJS 1.6.x, kudos! There’s not much that’s needed to get an app running with 1.5 to work with 1.6.

But, a very common error that starts appearing in people’s consoles after this upgrade is the dreaded ”Possibly unhandled rejection” error.

Did you see this and wonder what does this mean? And more importantly, how the hell do you get rid of it? Worse, if you just follow the highest voted answer on StackOverflow you probably hide bugs and won’t be aware of it. Read on for the full gist.

What does this error mean?

Essentially, this error is printed to the console whenever a promise in your app is rejected (resolved with a failure), and there’s no .catch() block to handle that failure.

AngularJS added this to nudge developers to always handle possible errors. This is much like always adding an else to every if to make sure you handle error cases, etc. After all, every unhandled rejection might be an error that you forgot to account for, a bug waiting to happen.

A very common reason for it to come up in apps is things like modals being dismissed or “canceled”, and since the app has nothing to do in those scenarios the rejected promise goes unhandled.

Getting rid of it – the icky way

The Stack Overflow answer I linked to above basically shows you how to disable this new behavior introduced in 1.6:

1
2
3
app.config(function ($qProvider) {
    $qProvider.errorOnUnhandledRejections(false);
});

You might be tempted to just reach for this quick fix, but be aware that it doesn’t 100% resemble that behavior in 1.5.

The catch

Say that you make a simple typo, like we all do like 20 times a day. For example, you’re handling a response from $http that looks like this: {value: "stuff"}.

Now, spot the typo:

1
2
3
$http.get('/stuff').then(function(result) {
  handleResult(result.valye)
});

Of course, I mistyped value as valye. This code in version 1.5.x would result in an error in your console. So would it in 1.6, unless you turn off unhandled rejection errors. If you do it, this error would be swallowed, and you’ll be spending lots and lots of time debugging stuff.

That’s why I recommend solving this the right way, even though it might take more typing, unless you really really have no other choice.

Getting rid of it – the right way

Well, the trick to not having unhandled rejection errors is… well… handling them.

You should, after every every promise with a .then() block have a .catch() block (and no, .finally() blocks don’t help here):

1
2
3
4
5
$http.get('/stuff').then(function(result) {
  // ... stuff here
}).catch(function() {
  // handle errors
});

And what about cases where you absolutely don’t care about errors? The convention I’d have to recommend is explicitly handling errors in a way that says you’re aware of possible errors and don’t care:

1
2
3
$http.get('/stuff').then(function(result) {
  // ... stuff here
}).catch(angular.noop); // <-- look here!

We’re passing as a handler the angular.noop function, which a function that does… nothing. It’s the equivalent of function() {}. This saves you some typing, and whenever I see noop as opposed to an empty function, I know it was left there by intention, and not that someone forgot what they were doing half way through a commit.

Yeah, this is some more typing, but it’s all in favor of making more robust and bug-free apps. And I recommend setting up a shortcut in your favorite editor to add this (e.g. my TextExpander is set up to write .catch(angular.noop) whenever I type ;noop).

Happy erring!

“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!

Advanced ng-model Integration for Bug-free Controls

| Comments

In the last post I explained that developers have a lot to gain by making sure their custom controls work properly with ng-model. That post shows the starting point – making trivial things like the required validation work and having the form’s $valid property take into account custom controls.

But that’s just the tip of the iceberg, and ng-model allows for quite a bit more customization and integration in order to allow writing controls that work as smoothly as builtin ones.

In this post I’ll how to start integrating your control with the NgModelController and make your controls more capable and robust.

Our Starting Position

Let’s keep going with the example from the previous post, which was this very simple component:

1
2
3
4
5
6
angular.module('app').component('myCustomControl', {
  template: '...',
  bindings: {
    value: '=ngModel'
  }
});

A good first step would be to note that we can use this control with the name attribute, since ngModel looks for it:

1
2
3
4
<form name="$ctrl.form">
  <my-custom-control ng-model="someValue" name="foo">
  </my-custom-control>
</form>

By supplying name="foo" we can now access it from the form to make sure it’s valid, e.g. $ctrl.form.foo.$valid.

Changing Values Properly

In order to make our component work seamlessly with ng-change we will need to make sure that whenever the control’s value is changed as a result of a user interaction (not programmatically), we let NgModelController know.

First, we will need to make sure to require the NgModelController, and then, when the user clicks a button, invoke $setViewValue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
angular.module('app').component('myCustomControl', {
  template: '...',
  require: {
    ngModelCtrl: 'ngModel'
  }
  bindings: {
    value: '=ngModel'
  },
  controller: function() {
    var self = this;
    self.userToggledOn = function() {
      self.ngModelCtrl.$setViewValue(true, 'click');
    }
  }
});

The important bits here are the require definition and the handling of the user’s click in userToggledOn, which calls NgModelController’s $setViewValue. Note that second parameter which lets it know what kind of DOM event triggered the change.

Defining emptiness

In my previous post I showed how required can simply be dropped in and used once ng-model is in place. That’s only the case, though, if your definition of “emptiness” matches the default logic as described in the documentation. But in case your control’s logic doesn’t match this, e.g. your model is an array and emptiness means the array is empty, you should override this behavior to let NgModelController know what you expect.

Inside your controller, after requiring ngModel as shown above, do this:

1
2
3
4
5
6
7
8
function SomeCtrl() {
  var self = this;
  self.$onInit = function() {
    self.ngModelCtrl.$isEmpty = function(value) {
      return !value || value.length === 0;
    };
  };
}

As you can see, we’re overriding the $isEmpty method, which is intended exactly for this purpose.

Also, note I’m making sure to access ngModelCtrl on $onInit, since it will not be defined earlier.

Handling Programatic Changes

An important part of the integration is to make sure the view is changed whenever the model value gets changed programmatically. For example, if the control’s ng-model attribute is a binding from its parent, and the parent changes that value, it usually means that the control should update the UI in order to show this state (e.g. because an update was received from the server).

In those scenarios, NgModelController expects us to override the $render method. NgModelController places a $watch on its value, and calls $render when it needs to change (though, note this watch is a shallow watch. If you’re mutating an object as your model, you will need to trigger it manually).

This would look roughly like so:

1
2
3
4
5
6
7
8
9
function SomeCtrl() {
  var self = this;
  self.$onInit = function() {
    // ... previous stuff
    self.ngModelCtrl.$render = function() {
      // Update the UI according to the self.ngModelCtrl.$viewValue
    }
  };
}

That’s it for now. There’s much more to ng-model, e.g. parsers and formatters that are handy when you want specific validations on inputs, etc. To be updated when I write about it, subscribe in the form 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!

You’re Not Using ng-model Enough

| Comments

You sit down and start your next frontend task. Likely, that task will require you to get input from the user. Maybe you’re adding a comments section, maybe it’s a little survey, or some simple form.

Handling inputs and forms with AngularJS can be a breeze, especially since AngularJS provides a lot of tools for efficiently doing just that. You need to add validations, like making sure the user filled all the fields, and it’s just a matter of adding a required attribute to all controls and checking the form’s $valid property.

I love it when instead of writing lots of code I simply do something along the lines of:

1
<button ng-disabled="!$ctrl.form.$valid" type="submit">Save</button>

(I’m not missing an ng-click there BTW, for submitting a form you should usually have ng-submit on the <form> element.)

But, what happens when you have use some custom control, instead of the browser’s builtins (e.g. inputs, textareas, selects)?

It usually looks ends up looking like this:

1
<my-custom-control value="$ctrl.someValue"></my-custom-control>

And suddenly, you can’t just use required, and the form’s $valid doesn’t work, and instead of using $ctrl.form.$valid to check everything is filled you have to write code along the lines of:

1
2
3
function isValid() {
  return self.form.$valid && self.someValue !== '';
}

Custom Controls Don’t Have to Suck

You don’t have to leave the comforts of Angular’s forms just because you have a custom control. You just need to make sure to wire things up properly.

It’s as easy as using ng-model to pass the value to the control, instead of some binding of yours. Here’s an example of refactoring a component to use ngModel:

1
2
3
4
5
6
angular.module('app').component('myCustomControl', {
  template: '...',
  bindings: {
    value: '=ngModel'
  }
});

You can still use the same name the component used before, such as value above, but make sure that the external name is ngModel. Using that component looks like so:

1
<my-custom-control ng-model="$ctrl.someValue" required>

Just by using the ng-model attribute, Angular’s ngModel directive will be used, and sprinkle its own magic. That means that it’ll register with the parent form, and add the needed validations.

For example, the required above will simply work now, and so will our original button, no need for custom code.

So please don’t write custom controls like an animal. Use Angular properly.

“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!

AngularJS Form Properties Guide

| Comments

Essentially every single web application being developed out there gets inputs from its users. Maybe it’s got a comment feed with a few text boxes. Or maybe it has some sort of calculator with different inputs and sliders. Of course, there’s almost always the login page. Yes, the email and password are inputs as well.

When working on web apps you’re going to be handling inputs quite a bit, and if so, you should be well equipped to use the right tools for the job. With AngularJS, those tools should include the extensive support for forms, inputs and validations.

I’ve covered the basics of writing forms before, but in this article I’d like to point out how Angular’s forms have a few magic properties that are worth knowing, since they can spare you some bugs and code!

First Things First: Getting Access to the Form

Forms in AngularJS have special properties, but how exactly are you meant to get access to these forms? The trick is to name the form. Once provide a name for your forms, AngularJS will automatically expose it under that name in your scope.

For example, say that we have this as part of the template of a component with $ctrl as its controller-as name:

1
2
3
<form name="$ctrl.exampleForm">
  <!-- inputs etc. go here.. -->
</form>

Setting the name to $ctrl.exampleForm means that in the template we can get access to the form, simply by using $ctrl.exampleForm. It can also be accessed from the controller’s code, using this.exampleForm.

Now that we know how to get access to the form, let’s start making use of it!

Testing Whether the User Has Interacted With the Form

A very common use case is the need to display certain error messages or help tips only after the user has started changing values in the form (or hasn’t started yet).

To do just that, forms in AngularJS come supplied with two handy boolean properties, $pristine and $dirty. These two booleans are always the negative of the other (i.e. $pristine === !$dirty).

When the form is in its virgin state and the user hasn’t changed anything yet, $pristine is set to true. Once the user has interacted with the form’s inputs, $pristine is set to false and $dirty is true.

In case you need to programmatically force the form back to its pristine state (e.g. the user clicked on reset, or after a successful save), you can call $ctrl.exampleForm.$setPristine().

Display Things After Form Submission

Sometimes, we want form validations to only be displayed after the user has clicked the save button, instead of changing as the user types or moves between fields.

In those cases, simply hiding validations until the form becomes $dirty won’t do, which is exactly why forms also have the handy $submitted property. This property gets set to true once the user has submitted the form, even if the form is invalid.

Submitting a form means clicking a button that has the attribute type="submit", or pressing Enter/Return inside an input.

AngularJS won’t prevent the form from being submitted if it’s invalid, meaning your ng-submit callback is called. You need to make sure not to act in case the form isn’t in a valid state.

Checking if the Form Is Valid

And just in order to check whether the form is valid or not, forms come equipped with a few more swanky properties.

First of all are the $valid and $invalid couple. If $valid is true – go right ahead. If $invalid is true – something is amiss.

In case the form is invalid, the form’s $error hash will contain all the necessary information about which fields are invalid and for what validations.

But, there’s another state here, which is when both are undefined. This is possible when the form has asynchronous validators. This means that it’s important to test these are true or false and not just “falsy” (e.g. undefined or null).

You can also check whether the form is currently pending, and see which of the validators are being processed, by accessing the $pending hash (which is structured similarly to $error).

There’s lots more that can be written about forms and their inputs in AngularJS, so if you’d like to hear more please subscribe 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!

Prevent AngularJS Injection Errors With Strict Mode

| Comments

I have to say that some of the most annoying production bugs I’ve encountered with AngularJS apps are the injection errors: Unknown provider: tProvider <- t. That error message is actually Angular trying to tell us that it doesn’t know where to inject t from, yet of course my code doesn’t have an injectable dependency called t.

These errors almost never show up during development, but only after building the app for release. That’s because these problems are the result of minification, which usually isn’t setup to run during development (for easier debugging and faster build times).

In most online examples we’d simply write an injectable like so:

1
2
3
app.factory('Foo', function($http) {
  // Angular will inject $http
});

But, after minification Angular won’t be able to tell what to inject because argument names get mangled. That’s why Angular has its special injection annotations, e.g.:

1
app.factory('Foo', ['$http', function($http) { ... }]);

Or:

1
2
function Foo($http) {}
Foo.$inject = ['$http'];

These are of course a PITA to maintain, which is why I strongly recommend using an automatic took like babel-plugin-angularjs-annotate. But even when using it, one can forgot to write the 'ngInject'; directive somewhere.

In those scenarios, you’d normally only understand that there’s a problem in production, which is too late (and if you’re testing properly, it just means you’re pushing bugs too late into your development process anyway).

The Solution: Strict Mode

Strict mode is a configuration of Angular’s $injector, telling it not to accept injectables that aren’t annotated, even when running in development. (Note, this should not be confused with ES5’s strict mode.)

You set it by adding the ng-strict-di attribute to your ng-app element:

1
<body ng-app="app" ng-strict-di>

When you turn this switch on, AngularJS will throw an error whenever it encounters an injectable that’s missing proper annotations, even in development. This means you’ll get a clearer error that’s a lot easier to track down and at the right time. It does mean, though, that you should make sure to run your automatic annotator in development as well (which should be easy with babel-plugin-angularjs-annotate).

Switch strict mode on and save yourself some nasty debugging!

“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!

ng-annotate Deprecated: What That Means for Your Projects

| Comments

For several years, the handy ng-annotate has helped save countless developers hours and debugging sessions, by automatically inserting Angular’s dependency injection annotations to code instead of developers having to maintain them by hand.

But, alas, it has been deprecated. Read on to understand what that means for your projects currently using it.

You Don’t Need to Type Annotations by Hand

Just because ng-annotate is deprecated it doesn’t mean it’s going to stop working. If it’s working for you right now, you can keep using that version and things should work as they have.

But, the “official” successor of ng-annotate is babel-plugin-angularjs-annotate (BPAA).

BPAA is actually a fork of ng-annotate that has been around for a while. While ng-annotate operated on JS source files, and had to be passed ES6 code after transpilation, BPAA is a Babel plugin and so operates as part of the transpilation process, and in a relatively transparent manner.

Given a build process/toolchain that’s already set up to use Babel, adding BPAA is often easier than adding ng-annotate. How much easier? You can follow the README I linked to above, but it generally boils down to a single NPM dependency and modifying a single line in your Babel configuration.

Since ng-annotate will no longer be maintained, I recommend switching to BPAA once you get the chance (nothing urgent though). I went through this process at a recent client and it was fairly simple and took less than an hour.

It also seems that BPAA is faster than ng-annotate, which makes for faster build times, that’s a small win.

Implicit Detection Considered Harmful

As part of the deprecation, the maintainers of both ng-annotate and BPAA agreed that implicit annotation detection is a bad practice and should be avoided.

What’s does that mean? Implicit detection means that the tools try and understand from the code whenever an Angular injectable is being declared and automatically insert annotations for it as needed, without any manual steps necessary by developers.

The maintainers talk about implicit detection causing a lot of problems, and recommend running BPAA with the explicitOnly setting. That setting means that you will need to markup class constructors like so:

1
2
3
4
5
6
class SomeCtrl {
  constructor($http) {
     'ngInject'; // <-- BPAA looks for this
    // ...
  }
}

And same goes for injectable functions:

1
2
3
4
5
angular.module('app').config(function($httpProvider) {
  'ngInject';

  // ...
});

This might seem weird if this is the first time you come across explicit mark up, but it’s easy enough and, along with strict-di, makes it impossible to come across injection bugs in runtime. While it would’ve been great if these explicit markers weren’t necessary, but since in most projects there’s at least one use case where they’re required I can see the logic of choosing the explicit route for safety.

“But I’m Not Using ES6!”

Well, surprisingly enough, you can incorporate Babel into your build process with BPAA as described above, to replace your existing ng-annotate step.

Babel doesn’t have to transpile ES6. If you don’t provide it with an ES6 (or ES2015, or whatever) preset, and just configure the BPAA plugin, Babel can be used instead of ng-annotate.

Open source sometimes moves fast, but the important point is that there’s a clear and relatively easy way to start using the supported tool (BPAA), and keep saving countless bugs and keystrokes!

“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!

Fixing Angular Template Overuse

| Comments

Templates in AngularJS are quite powerful, but just because something can be done in the template doesn’t mean you should do it there. The separation between controller and template can be vague in Angular, which is why I have a couple of guidelines which I consider to be best practices in any Angular project.

One of those rules of thumb is: Don’t do in the template what can be done elsewhere.

In this post, I will go over the different symptoms of template overuse and why it can be problematic.

Template Overuse Symptoms

Template Overuse (noun): Performing work in the template that will be better placed somewhere else.

ng-click

A popular example is ng-click abuse:

1
2
3
<button ng-click="$ctrl.validate() && $ctrl.submit()">
  Submit
<button>

As you can see, the ng-click handler is first making sure that the form is valid before actually calling the submit operation.

This extra logic is misplaced.

A similar, yet different, example is:

1
<button ng-click="$ctrl.counter = 0">Reset</button>

As a guideline, template event handlers should trigger functions on the controller, and not any other expression/statement (e.g. the above would be ng-click="$ctrl.reset()").

This goes the same for similar directives, such as ng-submit, etc.

ng-init

This, for some reason beyond me, is not an uncommon sight:

1
<div ng-init="$ctrl.setup()">

In case you’re not aware of it, ng-init simply runs the code given to it when the template initializes. Its valid use cases are so rare, even the documentation warns against using it.

And yet, a GitHub search comes up with 785K+ hits at the time of this writing.

There’s a good place to initialize stuff: the controller’s $onInit hook!

Underusing CSS

All the power we get from templates might make it easy to forget that not all visual logic has to be done inside them. A good example is using ng-if or ng-class for targeting special cases that can be handled in CSS, like special casing the first element in a list, or coloring every other row, like I’ve shown here.

Forgetting the Basics

I see too many developers reinventing the wheel instead of making use of the power of the web.

Consider the first example here, which showed ng-click="$ctrl.validate() && $ctrl.submit()". There’s a known mechanism for preventing actions on buttons in case the form state is invalid, which is setting those buttons to be disabled. This can be done by using Angular’s validators, or even simply by using ng-disabled:

1
2
3
<button ng-disabled="!$ctrl.validate()" ng-click="$ctrl.submit()">
  Submit
</button>

Why is Overuse Bad?

First, having extra logic in the template makes it harder to refactor code at a later point. I’ve yet to come across the perfect IDE that makes refactoring templates as smooth as refactoring JavaScript code. If that’s the case, I opt for the style that makes refactoring easier.

Further, it’s very easy for templates to contain a bunch of dense expressions and overly long ng-if conditions. These in turn make code maintenance a PITA. Templates should be easy to change when view requirements change, and are best when they make it easy to understand and visualize what is happening on screen. The more code in your templates, the harder they become to follow.

Also, logic in template also makes it harder to properly unit test controllers. For example, if the template contains an ng-init hook, then usually the test would also have to invoke whatever expression the ng-init is calling.

Essentially, all these reasons boil down to complicated templates making code maintenance harder in the long term. Making a point of keeping templates succinct will make for a codebase that everyone will be happier working on.

Fixing Overuse

To repeat, the basic guidelines you should strive to follow are:

  1. Don’t do in the template what can be done elsewere.
  2. Template event handlers should trigger functions.
  3. Remember the basics, like CSS and HTML form validation.
  4. Don’t use ng-init. Just don’t.

“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!

The Magic Properties of Angular’s ng-repeat

| Comments

One of the basic building blocks of Angular is, of course, the ng-repeat directive. It’s certainly one of the things newcomers pick up right when starting to learn Angular. Yet, it’s very easy to just learn the basics and miss out on some of its lesser known but useful features.

In this post you will learn what automatic properties ng-repeat creates on the scope object, to make common tasks easier.

$index

The scope property is most probably the most popular one. When using ng-repeat, every block of repeated content has access to a property called $index. This property is a number, and contains the current index of the “loop” ng-repeat is doing:

1
2
3
<div class="task" ng-repeat="task in $ctrl.tasks">
  <span ng-bind="$index"></span>: <span ng-bind="task.name"></span>
</div>

As you can probably guess, this will display next to each task’s name its index in the $ctrl.tasks array.

Yet while it is most known, it is probably the one that should be used the least.

$first and $last

It’s common when using ng-repeat to add specific behavior to the first or last element of the loop, e.g. special styling around the edges.

I’ve seen too many programmers do it awkwardly using $index:

1
<div ng-if="$index == $ctrl.tasks.length - 1">Last</div>

Instead, ng-repeat already supplies you with two ready boolean properties. $first is true for the first element, and $last is true for the last element.

While we’re at it, I’ll mention that if all you’re doing here is styling, e.g. ng-class according to the first/last index, you might be better off doing this purely in CSS using the :first-child and :last-child pseudo-classes.

$middle

This simple property is simply used to tell whether the current element is neither the first element in the loop, nor the first.

It’s equivalent to !$first && !$last (to please the logic nerds, this is also !($first || $last), according to De Morgan’s Laws).

$odd and $even

These properties simply state whether the current $index is odd or even. It’s very common to style grid with alternating colors between rows for easier readability, and if you’re using ng-class to add an .even class, you’d better use $even instead of $index % 2 == 0.

Yet, again, I’ll say that in case you’re using this solely for styling, doing this in CSS would probably be the better choice, e.g. :nth-child(odd) and :nth-child(even).

You can read more about these properties and ng-repeat’s other features in the documentation.

“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!

Understanding Optional AngularJS Bindings

| Comments

It seems that few developers are familiar with the ability to mark bindings in AngularJS as optional, and what that even means. After all, you may have seen that even when you don’t pass a binding to a directive/component, things seem to work, so why bother?

In this post we’ll go over the different binding types and understand what, exactly, setting them as optional means, so you’ll know when to use it and what might happen if you don’t.

Two-way = bindings

Two-way bindings, when not specified as being optional, would only complain when the component tries to assign to them. That is because, by definition, Angular would attempt to update the parent component as well. If the parent never gave a value, Angular will not be able to update it, and so would throw a runtime exception.

For example, consider a component called userEditor that defines its bindings like this:

1
2
3
bindings: {
  user: '='
}

If you were to instantiate the component without the user bindings, e.g. <user-editor></user-editor>, at first nothing bad would happen. The component’s controller, when accessing the this.user property, will see that its value is undefined.

But, if the component would ever attempt a reassignment, such as this.user = {}, it would result in an error.

In those situations, in case the binding is not mandatory, you should specify it as being optional:

1
2
3
bindings: {
  user: '=?'
}

Then, reassigning would just silently work, without actually updating anything in the parent.

Though, I will stress that reassignments should be rare and you should probably be using one-way bindings, described below.

One-way < bindings

Since one-way bindings are, hmm, one-way, they don’t have the same problem as described above on reassignments. Angular just doesn’t care that the component is reassigning, and so, for these bindings, the behavior would be the same with or without the optional setting. If the user of the component does not pass a value for the binding, it would be undefined in the component’s controller and that’s it.

Yet it can be specified as being optional, by providing the value <? in the bindings definition (or scope if you’re using a directive).

Expression (function) & bindings

The & binding is mostly used in order to pass a callback to a component–a simple function that can be invoked by the component’s controller.

By default, when these bindings aren’t set up with an initial value by the parent component, they simply translate to a function that does nothing and returns undefined (also known as angular.noop). This makes it completely safe for the child component to call that binding without risking a crash, as a nice example of the Null Object pattern.

And yet, sometimes the component would need to know whether the callback was passed at all or not, e.g. to decide whether certain work needs to be done.

In those scenarios it is possible to specify the binding as being optional, with &?, and then it is passed to the controller as undefined, instead of angular.noop. Then the controller can check for it, e.g. if (this.callback), and take action according to the value.

This does mean, though, that the controller can no longer blindly invoke the binding; it has to check first that it is defined.

Bottom Line: Use Optional If You Mean It

As you might understand, one can do Angular for quite a while without ever having to use optional bindings. And yet, I would argue that they should always be used if, indeed, the bindings are optional.

Also since in two-way bindings it would be a bug waiting to happen to do otherwise. Yet mainly because I believe that doing so helps to write better, more maintainable code, that is later easier for future users of the component to reason about and understand (be those users other developers or future-you).

By adopting a convention across your codebase to always specify optional bindings when necessary, your team will have another aid for explicitly documenting your code.

Just remember to add that ? to the binding definition!

“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!

Converting Angular Controllers to ES6 Classes

| Comments

After covering the process of transforming Angular services to ES6 classes, and digging into how injection works with ES6 classes, it’s time to take a closer look at controllers.

Controllers, being the basic building block in Angular, is also where you’ll get your biggest bang-for-the-buck for making the ES6 transition, in my opinion. That is, assuming your project is already making use of controller-as syntax, as opposed to exposing all of your controllers’ logic directly on the $scope object.

Let us look at an example controller:

1
2
3
4
5
6
7
8
9
10
11
12
function UsersCtrl(UsersStore) {
  var self = this;
  self.$onInit = function() {
    UsersStore.get().then(function(users) {
      self.users = users;
    }):
  };

  self.updateUser = function(user) {
    UsersStore.updateUser(user);
  };
}

(Note how all initialization is taking place inside the $onInit hook, which is a must starting from 1.6+)

Here is how the above controller would be written as a class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class UsersCtrl {
  constructor(UsersStore) {
    this.UsersStore = UsersStore;
  }

  $onInit() {
    this.UsersStore.get().then(users => {
      this.users = users;
    });
  }

  updateUser(user) {
    this.UsersStore.updateUser(user);
  }
}

The conversion steps are:

  1. Change the function to be a class definition
  2. Create inside it a constructor and move all injections to be its parameters. Inside this constructor assign every injected service to be a member of the new class.
  3. Change every method declared previously as this.function to be declared as a regular method in a class.
  4. Update all references to injected services to use the local properties, e.g. this.UsersStore instead of UsersStore.

Keep in mind that while classes have a constructor, you should not be using them for any real logic. You want to keep them as stupid as possible, only making sure to save injections. All other logic should be kept inside the $onInit hook.

Also note that ES6 classes do not have a concept of private methods/members. In the function syntax you could have declared a var innerData inside the UsersCtrl function, and innerData would not have been accessible to the template. That’s not the case with classes, and so I usually opt for a naming convention where templates are not allowed to access any methods or members on the controller that start with a _, e.g. _innerData.

You might find it icky, but that’s just the way it is. TypeScript, which you might also be considering, supports private members and even saves you the constructor boilerplate.

“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!