In this article we’re going to look at upgrading your first AngularJS (1.x) component, a simple todo app, across to Angular (v2+) code. We’ll compare the API differences, templating syntaxes and hopefully it’ll shed some light on upgrading to Angular, and well as making it appear less daunting.
We’ll be rewriting this small component in Angular, so let’s look at the existing functionality:
Add items to todo list
Ability to delete items
Ability to mark items as complete
Display count of incomplete and total todos
Let’s look at the source code to understand exactly how it’s built and what’s going on.
The HTML is extremely simple, a <todo> element.
The todo.html contents, a simple template that holds the UI logic to repeat our todo items, manage all submit/deleting functionality. This should all look pretty familiar.
The app is complete below:
One of the design patterns I highly recommend is using the controllerAs syntax (see my article here on it) inside the Directive definition, this allows our Controllers to be free of injecting $scope and adopt a more “Class-like” way of writing Controllers. We use the this keyword to create public methods which then gets bound to the $scope automatically by Angular at runtime.
Using controllerAs, IMO, is a crucial step to preparing AngularJS components for migration to Angular, as the way we write components in Angular uses the this keyword on an Object definition for our public methods.
Files to include, and boostrapping the application.
We’re going to walk through every single part of the setup of AngularJS versus Angular, from bootstrapping the application to creating the component, so follow closely.
We have the basic HTML page, including version 1.4.7 of AngularJS, and manually bootstrapping the application using angular.bootstrap.
We’re going to actually create the Angular application component in ES5, there will be no ES6 and TypeScript because this will let you write Angular in the browser with ease, and also the final working example is using ES5 running in JSFiddle.
There will, however, be the TypeScript/ES6 example at the end to demonstrate the full migration from 1.x to ES5, then the final ES6 + TypeScript solution.
First we need to include Angular, I’m not going to npm install or mess about installing dependencies, how-to steps are on the angular.io website. Let’s get up and running and learn the framework basics and migrate our AngularJS app.
First, we need to include Angular in the <head>; you’ll notice I’m using angular2.sfx.dev.js from version 2.0.0-alpha.44. This .sfx. means it’s the self-executing bundled version, targeted at ES5 use without System loader polyfills, so we don’t need to add System.js to our project.
So far everything is super simple, instead of window.angular we have window.ng as the global namespace.
Upgrading the Directive to an Angular component.
In Angular, we create a Todo variable, which assigns the result of ng to it with corresponding chained definitions (Component, Class) - these are all new in Angular.
Inside .Component(), we tell Angular to use the selector: 'todo', which is exactly the same as .directive('todo', todo); in AngularJS. We also tell Angular where to find our template, just like in AngularJS we use the templateUrl property.
Finally, the .Class() method is what holds the logic for our component, we kick things off with a constructor property that acts as the “constructor” class. So far so good!
Next, it makes sense to move our Controller logic from AngularJS across to Angular’s .Class() method. If you’ve used ReactJS, this will look familiar. This is also why I suggest using controllerAs syntax because this process will be extremely simple to do.
Let’s look what we have in our todo component already. Public methods use this to bind to the $scope Object automatically for us, and we’re using controllerAs: 'vm' to namespace the instance of the Controller for use in the DOM.
Now, let’s kill the Controller entirely, and move these public methods into the .Class() definition inside Angular:
Learnings here: “public” methods become properties of the Object passed into the .Class() method, and we don’t need to refactor any of the code because in AngularJS we were using the controllerAs syntax alongside the this keyword - seamless and easy.
At this stage, the component will work, however the template we have is completely based off AngularJS Directives, so we need to update this.
Here’s the entire template that we need to migrate to the new syntax:
Let’s be smart and attack this in chunks though, keeping only the functional pieces we need. Starting with the <form>:
Key changes here are the new (submit) syntax, this indicates that an event is to be bound, where we pass in $event as usual. Secondly, we are no longer needing a Controller, which means controllerAs is dead - note how the vm. prefix is dropped - this is fantastic.
Next up is the two-way binding on the <input>:
This sets up two-way binding on ng-model, also dropping the vm. prefix. This fully refactored section of code will look like so:
Moving onto the list of todo items. There’s quite a lot going on here, the ng-repeat over the todo items, a conditional ng-class to show items completed (crossed out), a checkbox to mark things as complete, and finally the ng-click binding to delete that specific todo item from the list.
The differences here are mainly in the ng-repeat syntax and moving across to ng-for, which uses #item of Array syntax. Interestingly enough, $index isn’t given to us “for free” anymore, we have to request it and assign it to a variable to gain access to it (#i = $index) which then allows us to pass that specific Array index into the deleteItem method.
Altogether we have our finished Angular component markup migration:
Altogether our Angular component will look like so:
It’s important to note an additional directives:  property inside the .Component() method, this tells the component what Directives to include for us to use. We have used ng-for and ng-model which are from the CORE and FORM Directive modules, so we need to explicitly define them inside the Array as dependencies:
And that’s it! The working solution:
Check out the Angular cheatsheet, this is extremely handy when refactoring your templates from AngularJS to Angular.
ES6 + TypeScript version
Note how we’re using ES6 import, with TypeScript @ decorators (@Component), as well as the ES6 class syntax to define a new Class to be exported.
We’re also not using any browser globals (window.ng) which is fantastic, all dependencies we need get imported from 'angular2/angular2', even our directives:  dependency Array.