codelord.net

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

Angular’s .component - what is it good for?

| Comments

Note: This post was updated for the official release of 1.5

In my Angular 1.5 sneak peek I mentioned the new .component() method. A lot of people are quite excited about this method. But, as with anything new, there are open questions:

When should you use it?
Why should you use it?
What’s the difference between it and .directive()?

Today we’ll understand what exactly it does and whether it’s good for you.

Syntax sugar

First things first: this method is truly just syntax sugar for the good old .directive() method you know (and hate?).

There’s nothing you can do with .component() that you can’t do with .directive().

So what is it good for?

It aims to simplify the way we create “components” – which roughly means UI directives. It also pushes the community to use the defaults that have become best practices:

  • Components have isolated scopes by default
  • They automatically use controllerAs syntax
  • They use controllers instead of link functions
  • The bindToController option is on by default

If this sounds familiar, you might remember this is basically how I’ve recommended we write directive controllers in this post.

Show me the code: before and after

Here’s an example component directive:

1
2
3
4
5
6
7
8
9
10
11
app.directive('list', function() {
  return {
    scope: {
      items: '='
    },
    templateUrl: 'list.html',
    controller: function ListCtrl() {},
    controllerAs: '$ctrl',
    bindToController: true
  }
});

It’s a simple component directive, with an isolated scope, binding, and a controller.

Here’s how you’ll write it with .component:

1
2
3
4
5
6
7
app.component('list', {
  bindings: {
    items: '='
  },
  templateUrl: 'list.html',
  controller: function ListCtrl() {}
});

As you can see, not much has changed.
But, things are simpler and more straightforward.
Also, we get to enjoy some default and save on boilerplate: bindToController is the default, controllerAs is on and defaults to $ctrl (I would have prefered a nicer looking default, but it’ll do).
Another nice point is that we don’t need to write a stupid function that always returns the same object.
We just define that object right here.

When can/should you use it?

Clearly there are a few cases where you can’t/shouldn’t use it:

  • If you need the link function (though you rarely should)
  • If you want a template-less directive, e.g. ng-click that doesn’t have a template or separate scope

For all your other directives, this should work. And because it saves on boilerplate and less error-prone it’s nicer to use.

I usually prefer to go with whatever would work everywhere.
And here it goes against .component() that it can’t fully replace .directive().

But, using it saves so much boilerplate.
And, non-component directives should be rare, which means they’ll stand out even more.

That’s why, in my opinion, using this new syntax is worthwhile. You can read the full docs about it here.

Lots of new goodies

$onInit

It has been pretty common to group initialization code of your controllers inside some function. For example, John Papa’s style guide recommends an activate() function to fire promises.

With 1.5 there’s official support for this much-needed concept. If the controller has a $onInit function, it will be called once all bindings are initialized:

1
2
3
4
5
6
7
8
app.component('foo', {
  templateUrl: 'foo.html',
  controller: function() {
    this.$onInit = function() {
        console.log('Foo component initialized');
    };
  }
});

This will also make transitioning components to ng2 easier (since it has the equivalent ngOnInit function).

You can require other components/directives

In the first RC releases of 1.5, there was no (nice) way for using the awesome require property directives have. It meant that making a structure of components that communicate using nice APIs on their controllers wasn’t really possible.

But, with the final release we got it all sorted out:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
app.component('parent', {
  templateUrl: 'parent.html',
  controller: function() {
    this.helperFunc = function() {};
  }
});

app.component('child', {
    templateUrl: 'child.html',
    require: {
        parent: '^parent'
    },
    controller: function() {
        this.$onInit = function() {
            this.parent.helperFunc();
        };
    }
});

This greatly helps in writing maintainble Angular, as I’ve recommended (2 years ago!) here.

One way bindings

First, make sure you don’t confuse this with 1.3’s one-time bindings (or “bind once”).

One-way binding does half of what two-way binding does (surprisingly). Previously, we could pass objects to child directives/components with the = binding:

1
2
3
4
5
6
7
app.component('foo', {
  templateUrl: 'foo.html',
  bindings: {
    bar: '='
  },
  controller: function() {}
});

This would have created a two-way binding with the component’s parent. Whenever the parent would assign a new value, it would be propagated to the child. And vice-versa, if the component assigns a new value it will be copied to the parent.

This, while helpful, isn’t a very common scenario in my experience. That’s why Angular has finally introduced one-way bindings.

These create just a single watch, that watches for changes in the parent and syncs them to the child. We gain performance (by cutting in half the amount of watches created) and things become less error prone. This also is a step towards the way things will usually behave in ng2.

The syntax is similar:

1
2
3
4
5
6
7
app.component('foo', {
  templateUrl: 'foo.html',
  bindings: {
    bar: '<'
  },
  controller: function() {}
});

Yeah, we just change = to <.

All in all, .component() is a great addition to Angular. It’s a real upgrade for code quality and helps you preper for ng2.

So, upgrade to 1.5 and start using .component(): you have unlocked a new skill!

“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 1.5 is close - here’s the interesting parts

| Comments

The first release candidate of Angular 1.5 was just released. This means the official release is probably right around the corner.

It can be hard to keep up with the changes in Angular. Things change pretty fast. I know a lot of companies that haven’t even moved to 1.4 yet (or 1.3, or 1.2). And everyone are trying to at least keep an ear open for the upcoming changes in 2.0.

And now there’s another version looming. When will you ever find the time to learn what’s changed?

Well, fear not fellow Angularist! I’ve gone over the changelog and gathered the interesting points below. In five minutes you’ll know what’s all the new rage about. Here are the interesting bits:

.component method

This is a new addition to Angular modules, along .controller, .directive, etc. The new .component() method is actually syntax sugar for creating a directive in the now-standard manner. This makes it easy to create isolated directives, with controllers using controller-as syntax.

This is a great step in the right direction of setting a proper Angular style in my opinion. But, it comes with a few interesting changes we’ll have to get used to. I will be writing more in depth about this soon (subscribe below to get notified!). In the mean time, there’s an excellent write up of all the options in Todd Motto’s blog.

Multiple transclusion

Finally. This means we can, at last, create reusable components with several transclusion slots.

For example, we can create a modal dialog directive with set sections, such as header and body, and use it like so:

1
2
3
4
<modal>
  <modal-title>My title</modal-title>
  <modal-body>The body goes here</modal-body>
</modal>

Prior we had to do all kinds of workarounds to get a similar solution.

While you may not be writing directives that use transclusion daily, it will make the libraries we all use much more powerful.

You can see the relevant commit here.

Lazily compiling transclude functions

This change means that things like ng-if (which you’re using instead of ng-show where appropriate, right?) will now work faster.

It might come as a surprise, but if you were using lots of ng-ifs that were never displayed on screen you still took a performance hit in page load time.

Luckily, 1.5 will improve this, making workarounds such as lazy-ng-if no longer necessary.

Not a lot of breaking changes

You should really go over the changelog, but from what I see it seems like most projects will be able to easily upgrade.

The breaking changes are quite obscure and shouldn’t matter for most of us.

Yay!

So, it seems like 1.5 is going to be a nice release. I’ll keep you posted if the next release candidates have any more interesting points.

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

Lazy Loading Your First Component in Angular

| Comments

A lot of web developers that come to Angular from other MVC frameworks are surprised to learn that there’s no official support for lazy loading.

Lazy loading is the common practice of not loading all of your source files on initial load. This can speed up your load times significantly in certain cases.

While some frameworks support this out of the box, with solutions like RequireJS, Angular doesn’t play that easy.

Angular 1.x has no support for lazy loading. In fact every solution is kind of a workaround for the way Angular expects to work.

Why doesn’t Angular support this? Well, to be frank I’ve seen relatively large applications get great load times by minifying all code to a single file. A lot of the times lazy loading is just premature optimization.

But say that you’ve measured, checked and finally decided you really need lazy loading.

Introducing ocLazyLoad

The ocLazyLoad library is a popular open source project for adding lazy loading capabilities to Angular.

I like it because it’s simple and doesn’t require a lot of fussing around to get your first component lazy-loaded. You don’t need to start extracting extra modules and make big changes to the way you structure your app.

It is really the simplest lazy loading solution I know for Angular.

Example: A lazy loaded ui-router state

Say that you have a ui-router state that’s seldom used and uses some big file you’d rather not load until needed.

Once you include the ocLazyLoad source (e.g. using bower) the job is pretty easy.

First, make sure you’re not loading the lazy loaded files on initialization. For example, you might need to remove the <script> tag from your index.html file.

Now, let’s configure our state appropriately:

1
2
3
4
5
6
7
8
9
$stateProvider.state('lazy', {
  url: '/lazy',
  template: '<lazy></lazy>',
  resolve: {
    lazyLoad: function($ocLazyLoad) {
      return $ocLazyLoad.load('lazy.directive.js');
    }
  }
});

This little snippet uses resolve to load the needed files before loading our state. I don’t usually use resolve, but this is a nice use case.

You can load multiple files here. For example, load your directive along some external and big JavaScript plugin.

And that’s it. We actually didn’t need to write our lazy directive differently than any other directive.

That was easy, wasn’t it?

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

Query Parameters in ui-router Without Needless Reloading With Example Project

| Comments

You’re working on a new page in your app, one that should use query parameters. Query parameters give you support for deep links, bookmarks, browser history support, etc.

If you reach for doing this in ui-router without proper research, you’ll quickly stumble upon several traps. For example, you’ll notice that your state reloads on every parameter change. Or that some of the workarounds online completely break browser history.

Let’s see how we can do this the right way. In a previous post we saw how ui-router can be used for easily doing pagination and sorting in the URL.

Another popular use case is a text input whose value is synced as a query parameter, e.g. for search pages.

I’m pretty sure you don’t want your state to reload just because the user typed a character and the URL has updated from /inbox?search=a to /inbox?search=ab.

Serializing an input to a query parameter

First, our directive’s controller will have pretty much this logic:

1
2
3
4
5
6
7
8
9
10
var vm = this;
vm.searchText = $state.params.search;

vm.searchTextChanged = function() {
  $state.go('.', {search: vm.searchText}, {notify: false});
};

$scope.$on('$locationChangeSuccess', function() {
  vm.searchText = $state.params.search;
});

With this template:

1
2
3
4
5
6
<input type=text ng-model=inbox.searchText
  ng-change=inbox.searchTextChanged()>
<div ng-repeat="item in inbox.items
                | filter:{text: inbox.searchText}">
 {{ item.text }}
</div>

And this state configuration:

1
2
3
4
5
6
7
8
9
10
11
$stateProvider.state('inbox', {
  url: '/inbox?search',
  template: '<inbox></inbox>',
  params: {
    search: {
      value: '',
      squash: true
    }
  },
  reloadOnSearch: false
});

We define a state with an optional search parameter. Then we wire the directive to initialize the search input from the URL parameter, and make it update the URL parameter whenever the input changes.

We update the URL using $state.go() and are careful to pass {notify: false}. This prevents ui-router’s default behavior of reloading the state.

Preventing reloads on back button clicks

If you check the code now, you’ll see that whenever the search parameter changes due to back/forward presses in the browser the state still reloads. This might actually be OK for you, depending on your scenario.

But sometimes that’s unacceptable.

You can use the reloadOnSearch option to disable the reloading:

1
2
3
4
$stateProvider.state('list', {
  // Same as before, but add:
  reloadOnSearch: false
});

But this alone breaks browser history in this state. We need to watch for when the user changes the query parameters in the address bar, or presses the back button. You will need to listen for $locationChangeSuccess events:

1
2
3
$scope.$on('$locationChangeSuccess', function() {
  // Use $state.params to update as needed
});

Note: This will be triggered for address bar/back button URL changes, but also for changes we initiate from our code. Keep this in mind. Also, it seems that you can’t trust $stateParams when using this approach, as it will not get updated. You’ll need to use only $state.params.

Voila! You can play around with a live demo here.

Possible improvements

The current solution enters every character changes to the history. In case that’s not the right UX for you, you might want to look into minimizing history.

For example, we can only write some of the changes to the browser history, or even squash changes to the same parameter to a single item in the browser history.

If those scenarios interest you, leave a comment and sign up for my newsletter to get the post once it’s out!

“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 Deal With Angular And Minification

| Comments

Angular and minification (or “minifying” or “uglifying”) might seem like they don’t play well at first. First impressions are usually quite negative:

“It turns out that Angular dependency injections has problems if you minify your javascript code”
“I’ve read that uglifying can break Angular dependency injection”
“Can someone explain why the special syntax helps not screw it up?”
“Should I use $inject or wrap things in an array? Do it manually or use a tool?”

Truth is that by understanding how minification was meant to work with Angular you can set it up very quickly.

What’s minification

For years javascript developers have been using tools that obfuscate/minify their code. The reasons are usually a combination of wanting to reduce download size and preferring not to expose your code plainly for the entire world to see.

Minification usually involves renaming things to be shorter. So that this:

1
2
3
app.factory('Foo', function($http, $timeout, Something) {
  // ...
});

Becomes this:

1
2
3
p.factory('Foo', function(a, b, c) {
  // ...
});

Why minification messes with Angular

Angular’s dependency injection, by default, works by looking at the function’s argument names to determine what it should be injected with. Once minification mangles those names, Angular no longer knows what the function wanted. Sad trombone.

Special syntax for preventing this

You’ve probably come across at least one of the two possible ways to work around the minification problem.

The first is to wrap every injectable in an array that also names the different dependencies. For example:

1
2
3
4
app.factory('Foo', ['$http', '$timeout', 'Something',
            function($http, $timeout, Something) {
  // ...
});

This works anywhere that you’d pass in an injectable to Angular (e.g. controllers, directives, etc.).

Another way is by adding a $inject property to the injectable:

1
2
3
4
5
function Foo($http, $timeout, Something) {
  // ...
}
Foo.$inject = ['$http', '$timeout', 'Something'];
app.facory('Foo', Foo);

Both of these ways work because they encode the dependencies as strings, which are not changed by the minification process.

But don’t write this by hand like an animal

Yes, you can start rewriting your code to use the explicit syntax. But it’s error prone to copy this around, a glaring DRY violation and just tedious & boring.

If you have minification it means you already have some sort of build process in order.

In that case, it’s usually dead simple to add another step to the build process that automatically adds the explicit syntax to your code, you just need to provide it with little hints. Never waste a keypress.

The de-facto standard is to use babel-plugin-angularjs-annotate. It’s a very simple tool that you can just drop in as part of your build process, right before minification. It essentially just goes over your Angular code and rewrites it to have the explicit dependency injection syntax. I’ve written more about it here.

Voila. Configuring it will save you time and make your project minification-ready. w00t!

“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 controllerAs: When Should You Use Scope

| Comments

Angular 2 is getting closer and so controllers are getting closer to death.

Using controllerAs syntax is the agreed way to prepare your code for the migration. But with all its awesomeness it makes some basic tasks tricky. Injecting $scope seems to be frowned upon.

Should you never use $scope again?
What about broadcasting events?
Listening for changes with $watch?
In what situations should you use it?

We’ll answer these questions in this post.

Recap: Previously I showed you how to refactor your existing controllers to make them easier to migrate to components. Then how to stop using them entirely.

These changes all rely on the awesome controllerAs syntax in Angular. This allows us to treat our directive controllers as objects and not use a 3rd party object, the scope, to pass along information.

Yet, scopes still have valid uses in the Angular world.

$watch

Sometimes, though hopefully rarely, you gotta write a $watch. Prior to controllerAs you’d write: $scope.$watch('foo', ...) and access $scope.foo.

Now that properties are exposed on the controller, e.g. using vm.foo = '123', this requires tweaking.

If our controller has a foo property and its controllerAs name is vm we can write: $scope.$watch('vm.foo', ...). That would work.

But… eww. I find this aesthetically displeasing. Nothing in your controller’s code should be aware of the controllerAs name it’s got. That knowledge is usually only used in the template.

That’s why I prefer to write the slightly longer:
$scope.$watch(function() {return self.foo;}, ...)

This decouples the controllerAs name and the controller’s code.

Events and broadcasting

Scopes come with the $emit, $broadcast and $on functions for interacting with events. Whenever you’re certain that events are the right tool for the job (which they usually aren’t) you should use this mechanism.

But, in a lot of situations there’s no need to use specific scopes with hierarchy for passing events. For example, if you want a generic PubSub event bus you can just use $rootScope alone across your system. I show a pattern for this here.

$apply

We have to call $scope.$apply() when running code in non-Angular contexts, like a jQuery plugin callback.

There’s no escaping this. If your code required $apply before moving to controllerAs you’re gonna have to keep doing it.

Like the previous point, though, you can just use the $rootScope. Calling $apply on any child scope has the same effect as calling it on the root scope. Using the $rootScope in these situations makes it clear that it is being used for something global.

For anything else, don’t use scope

It’s that simple. Kick controllers’ butt!

“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 Forms and Validation: Step By Step Example

| Comments

We all have to get input from users, validate it and act on it. But everywhere I look I see developers that aren’t using Angular’s form capabilities to their full potential.

Yes, it requires learning the right buttons to push. Let’s spend a few minutes getting to know Angular forms. You’ll be surprised how easy it is to implement most of your forms’ UX.

In this post we’ll go through the steps of setting up a sign up form. We will implement a custom password validator and an asynchronous check that the supplied email address hasn’t been used yet.

The raw form

1
2
3
4
5
6
7
8
9
10
11
12
13
<form name=vm.form novalidate ng-submit=vm.signUp()>
  <label>
    Email:
    <input type=email name=email
           ng-model=vm.email required>
  </label>
  <label>
    Password:
    <input type=password name=password
           ng-model=vm.password required>
  </label>
  <button type=submit>Sign Up!</button>
</form>

This should look familiar. We have a simple form with 2 fields and a nice button. Some things to note:

  • We’re adding a name attribute to the form and inputs so they’ll be bound to our controller. We will use this shortly below.
  • Specifying required and type=email already gives us basic validation by Angular. But since some browsers have validation too we use the novalidate attribute so it won’t clash with Angular’s stuff.
  • Use ng-submit. You should always either pick ng-submit or have ng-click on the submit button, never both. I prefer ng-submit since I consider this action to be at the form’s level.

Prevent submitting an invalid form

This one’s an easy peasy. Just use ng-disabled to prevent our form from getting submitted when the state of the form is invalid:

1
2
3
<button type=submit ng-disabled=!vm.form.$valid>
  Sign Up!
</button>

Angular Weirdness Alert: It’s important to use !vm.form.$valid and not vm.form.$invalid here. An Angular form can be in a third state, pending, which we’ll talk about soon. This means that $valid != !$invalid. Le sigh. We don’t want to allow the form to submit in any state other than the valid state.

Playing with the form would now prevent us from submitting it if we don’t supply a value in both inputs.

Add password validation

Now we’re getting serious. Let’s add some basic validation: passwords should have at least 8 characters and contain both letters and numbers. We’ll use the awesome validators introduced in 1.3+. We’ll use a little directive that we’ll then attach to the password element:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
app.directive('password', function($timeout) {
    return {
      require: 'ngModel',
      controller: function($element) {
        var ctrl = $element.controller('ngModel');

        ctrl.$validators.password =
          function(modelValue, viewValue) {
            return viewValue && viewValue.length >= 8
              && /[0-9]/.test(viewValue)
              && /[a-z]/i.test(viewValue);
        };
      }
    };
});

And add that directive to the element:

1
2
3
<input type=password name=password
       ng-model=vm.password
       required password>

We’re placing our directive on the same input element with an ng-model and so we can gain access to ngModelController and add our validator. As you can see, validators are pretty straightforward.

Just add a function that gets called with the view value — the input’s value — and returns whether that value is valid or not.

You can now see the button remains disabled until you enter a valid password.

Add email validation with a loading indicator

We’d like to make sure the supplied email address hasn’t been registered with our site yet as part of the validation.

Starting from version 1.3, Angular has support for asynchronous validators. That means we can easily supply a promise and Angular knows to wait for the promise to either be resolved or rejected to tell whether the field is valid. Sounds like exactly the case where you want to query your server and make sure the field is valid.

Here’s our email validator directive:

1
2
3
4
5
6
7
8
9
10
11
12
13
app.directive('availableEmail', function(UserService) {
    return {
      require: 'ngModel',
      controller: function($element) {
        var ctrl = $element.controller('ngModel');

        ctrl.$asyncValidators.availableEmail =
          function(modelValue, viewValue) {
            return UserService.isValid(viewValue);
        };
      }
    };
});

This is pretty similar to the password validator. We’re using $asyncValidators instead of $validtors and our function now returns a promise.

While the validation is happening we’d like to show the user that something’s going on. Here’s a very basic change to our template:

1
2
3
<div ng-if=vm.form.email.$pending.availableEmail>
  Checking email availability...
</div>

As you can see, while an asynchronous validation is going on, Angular sets the $pending.availableEmail property on the form’s email attribute (the name email is coming from name=email on the input). Voila!

Adding error messages

Right now our form has all of its functionality implemented. It won’t allow submitting an invalid form.

But, of course we’d like to show the user what he’s done wrong, instead of leaving him to guess.

That’s why Angular introduced the ng-messages directive. Let’s see how we can add error messages to our email field:

1
2
3
4
5
6
7
8
9
10
11
12
<div ng-messages=vm.form.email.$error
     ng-if=vm.form.email.$touched>
  <div ng-message=required>
    Please specify an email address
  </div>
  <div ng-message=email>
    Please specifiy a valid email address
  </div>
  <div ng-message=availableEmail>
    Email address is already taken
  </div>
</div>

As you can see, ng-messages is a lot like ng-switch and lets us display an appropriate error message depending on the validator that’s failed.

Also note the ng-if=vm.form.email.$touched bit. We wouldn’t want the form to show these errors right when it first loads. But, when it’s empty it is already invalid.

Angular keeps track of whether the user has interacted with an input, i.e. focused it and then clicked outside of it. That’s what the $touched means. Awesomely simple!

Check out a live example of the resulting form here, and the source here. In it you can see a bonus: how to use Angular’s forms to style the error messages and the form in invalid states.

“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 Authentication: Remember Where You Were + Demo Project

| Comments

In my last post we saw the simple 3 step recipe for adding basic authentication in your Angular app.

This time let’s see how to get a very common feature to work: taking the user back to where he was before his session expired and he had to login again.

We all know that it sucks to suddenly get logged out and have to dig through lots of tabs to find the right page again. It turns out that getting something like this to work with Angular takes only a handful of code changes. But, it’s a bit tricky, so pay attention:

Background

We’re continuing off the previous post. This means that we already have an interceptor that redirects to the login page whenever a request fails with status 401.

Our Modus Operandi

You’ve seen apps where the login URL looks something like: foo.com/login?url=%2Fsome-page and similar. And we’ll be using something very similar.

Whenever our interceptor would transition to the login page it would first grab the current URL and pass it as a parameter to the login state. Our login state, upon success, will redirect to that URL.

The Interceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
angular.module('app').factory('AuthInterceptor',
function($injector, $location, $q) {
  return {
    responseError: function(rejection) {
      if (rejection.status === 401
       && rejection.config.url !== '/login') {
        var $state = $injector.get('$state');
        // This is the interesting bit:
        $state.go('login', {url: $location.url()});
      }
      return $q.reject(rejection);
    }
  };
});

As previously, it just listens for 401 responses and redirects to the login state. The important bit is that we take the current URL using $location.url() and pass it as a parameter to the login state.

Login State Logic

When the user clicks the login button we perform something like this:

1
2
3
4
5
6
7
8
9
10
11
vm.submit = function() {
  $http.post('/login',
       {user: vm.user, password: vm.password})
       .then(function() {
    if ($stateParams.url) {
      $location.url($stateParams.url);
    } else {
      $state.go('home');
    }
  });
};

Now you can see that we’re simply using that state parameter, when it’s present, to move to it.

The important part is to make sure that you encode the state of your pages in the URL. That’s it!

The live demo

You can see the live demo here and its source code is available here.

It’s a simple app with a couple of screens. Each screen has a button on the top to let you simulate a session expiring – it will cause a 401 to be returned which will then redirect you to the login page. You can see how after submitting the login you get right back where you were – even with URL parameters.

What if the URL isn’t enough?

Sometimes, though rarely in my experience, you want to restore the state with more than just the URL. For example, maybe the user has started typing something that you want to save.

In those scenarios I usually go with using a service to store the objects that I need. Then, when our interceptor redirects to the login page it can first shout out an event. Whatever page is currently displayed can catch the event and persist the state. See my post about notifying changes.

Since the last post I got several requests to discuss more features, like admin only pages, etc. Subscribe below so you won’t miss them and feel free to comment if you have other ideas I should add to the mix!

“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 Authentication: 3 step recipe

| Comments

Authentication seems like something that literally 100% of people using Angular would want to do. Yet you need to bake something on your own not knowing if your approach is right. Angular doesn’t come with any baked support for authentication. (Did that surprise you? It surprised me the first time I heard.)

But I have some good news for you: getting started with authentication is pretty easy. Here’s my basic recipe.

Say you have a simple username/password login. To login you make a request to the server which then either sets a cookie for you or returns some token you need to add to all your following requests.

To get started you need to take care of just 3 things:

  1. Logging in
  2. Passing credentials in every request
  3. Redirecting to login when the session expires

Let’s dig in.

Logging in

This is pretty straightforward. All you need to do is make a request that 99% of the time looks like this:

$http.post('/login', {user: user, password: password})

And that’s that.

Passing credentials

Depending on how your authentication works this will change a bit. If the server sets a cookie in the response to a successful login request, you’re done.

In other cases you get a token from your server. Then you need to make sure future requests use it. Your code will probably look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
module.factory('Authenticator', function($http) {
  return {
    login: function login(user, password) {
      return $http.post('/login',
                        {user: user, password: password})
        .then(function(response) {
          var token = response.data.token;
          $http.defaults.headers.common['My-Header'] = token;
      });
    }
  };
});

This makes sure that once you receive a good token you’ll pass that token along with all your future requests. Simple as that!

Redirecting to login

All your requests now pass their authentication token/cookie. Everything is fine.

But, eventually it will stop working, e.g. your sessions expire after 2 hours. What will happen if the browser was open the whole time?

Ideally, the next time you make a request, the server will respond with a 401 status code. That will let you know that you’re no longer authenticated.

Now, you just need to take the user back to the login page. Angular has a nifty tool just for this purpose: HTTP interceptors. These let you write code that executes whenever you get a certain response.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.factory('AuthInterceptor', function($injector, $q) {
  return {
    responseError: function responseError(rejection) {
      if (rejection.status === 401
            && rejection.config.url !== '/login') {
        var $state = $injector.get('$state');
        $state.go('login');
      }
      return $q.reject(rejection);
    }
  };
});

module.config(function($httpProvider) {
  $httpProvider.interceptors.push('AuthInterceptor');
});

What’s going on here? We’re creating an interceptor that looks at every failed response. If that response has the status code of 401 (unauthorized), we take the user to the login screen so he’ll… log in.

Two details you should take note of:

  • We’re explicitly not redirecting if the /login request itself returned 401. That’s because in that situation you’re probably already in the login screen and want to show a “Bad password” message and not reload the screen :)
  • Be careful to return $q.reject(rejection). If you don’t reject the return value, Angular would think you recovered from the error.

Done.

I bet this was simpler than you expected. The key takeaway here is that your Angular app is never supposed to make a decision about if you are logged in or not. That decision is left up to your API.

That’s great separation of concerns and it really makes things simpler on the client side.

“Wait! What about {some feature}?”

This is the basic recipe. With it you’ve got a working system you can actually start using right now.

There are endless tweaks to make: add a log out button, redirect back to where you were after login, special admin-only pages, persisting tokens in local storage, etc. I’ll be posting guides to some of these soon. Leave a comment if you’d like me to expand on any specific feature.

“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 Nitpicking: Differences between $timeout and setTimeout()

| Comments

When you just got started doing Angular you probably had a couple of times where you used setTimeout() without giving it much thought. Then, you noticed that something wasn’t working right. Changes weren’t happening when you expected them to happen.

If you’re lucky you had someone nearby that just said “Oh, you should use $timeout”. If you weren’t lucky you probably wasted 30 minutes until you found that out.

But I don’t like just knowing to use that. You should understand why you use $timeout.

Let’s up your Angular game and understand exactly what are the differences between these two.

Note: everything here goes the same for setInterval and $interval.

The basic difference: the digest cycle

Angular’s binding magic works by having a digest cycle mechanism. Whenever a change is made Angular goes over all the values that are bound and checks which have changed. It then updates all dependent values. Simple yet awesome.

The problem is that to be aware of when changes happen, i.e. when to run the digest cycle, Angular needs to know when asynchronous things happen. That includes user interaction, AJAX requests and, yes, timeouts.

This is why you use ng-click and not element.on('click') or $http and not $.ajax(). They make sure to trigger a digest after those asynchronous events were handled.

Same goes for $timeout. That’s why if you have a setTimeout function that changes your view you often don’t see the changes rendered until sometime later (when something happens to trigger a digest).

The benefits of $timeout integration

Unit testing

Using setTimeout in code you’d want to test is shooting yourself in the foot. You will have to either write very ugly asynchronous tests that depend on delays or monkey patch the global setTimeout function.

Angular’s $timeout is tightly integrated into the whole testing suite and ngMock. This means that whenever you’re testing code with $timeouts you know it won’t be executed until you specifically call $timeout.flush(). That will then call all pending timeouts synchronously. I know this just got you excited a bit.

Promises

$timeout returns a promise that resolves once the function has finished running (e.g. show a modal). This way if you ever need to do something once a timeout has run you don’t need to construct a promise yourself:

1
2
3
4
5
$timeout(function() {
  // Do something
}, 1000).then(function() {
  // You know the timeout is done here
});

The performance angle

Since $timeout triggers a digest, if it happens frequently it might cause performance issues. Sometimes, though rarely, the majority of your timeouts don’t change anything related to Angular. In those cases triggering a digest is a waste.

If you google you might find outdated “tricks”. Those will tell you that performance is exactly the use case for whipping out setTimeout. But, Angular’s $timeout has a third argument, called invokeApply. You can use it to… you guessed it… prevent $apply from running and avoid the digest.

So even if you ever happen to need to squeeze this a bit to make your app performant you can do something like this:

1
2
3
4
5
6
7
$timeout(function() {
  if (actuallyNeedToUpdateAngular()) {
    $scope.$apply(function() {
      // Do someting here
    });
  }
}, 1000, false); // <-- Note the 3rd argument!

Another use case is to use invokeApply in order to have a timeout that only triggers a digest on a sub-scope and not the whole scope hierarchy. This is very advanced voodoo, but you can see an example of that here.

tl;dr Use $timeout always

Yes, we have come full circle to the exact same knowledge you had before, but now you have understanding. Knowledge is power my friend!

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