You guys know how much I love to write beginner guides. This is one of those guides except that it’s on a library that is not really out, the API changes, and the documentation is kind of non-existent. If you notice any disrepair or outdated code, let me know.

I’m, of course, talking about Angular 2. Before I get into it, I wanted to mention how funny I find it that Angular 2 has been making rounds in the tech community ever since it was announced sometime in 2014. I’m sure it’s been at least a year.

Since then, developers have gotten used to the idea that Angular 2 will be like Angular 1 except more confusing, with a non-compatible API and it’s going to tank. “Go with Flux and React” they say (and I say onto them “Yeah yeah, the guide is coming for that one, too”) while switching which incompatible Flux library they’re working with that week (heard of Redux? No, not Reflux, not Flummox, Alt, Facebook Flux, or whatever, Redux. It’s different.)

Anyways, so Angular 2 gets a lot of flack while it’s still in its pre-infancy. And I think it’s undeserved. After trudging through my first Angular 2 app, I found myself liking it. Much more so than when I worked with React or Angular 1.

Note that the wealth of information on Angular 2 makes it nearly impossible for me to build an app through this article without making it into a novel-sized tutorial. As it stands, VIM tells me the article is over 7K words (including code samples).

What’s different (NOT!)

Real quick, let’s discuss what’s different. Wait, wait, too long of a list, let’s talk about what stayed the same:

  1. you still use HTML templates with Angular2 syntax on top of it for additional functionality
  2. there are still two-way bindings despite what you’ve been told
  3. directives exist (albeit in a different form)
  4. services are still there (but nothing else…well, kind of)
  5. AngularJS still has a DI of its own but you can circumvent it if you please.

There are a lot of things that changed, obviously. One of the biggest changes was slimming down the API. There are no more “providers” and “factories”, etc. Everything is just either Injectable (a class that can be DIed) or not. There is also no scope, everything is attached to your component class. There are no controllers. And everything is much faster.

note Actually, there are still factories and other types of services as I understand it but they’re low-level and should be used only in very specific instances. Think of them in terms of “private” (or Angular 2 internal) implementations. For all intents and purposes, you can forget about them.

Enter Angular 2

So what is Angular 2? Let’s look at its core and defining features:

  1. a full framework with support for forms, http requests, and much more. It’s a complete toolkit to building a web app.
  2. ES6 and TypeScript compatible meaning that it plays well with ES6 modules, class systems, and has type support.
  3. Smart Dependency Injection which can be used to pass in classes and services with specific configuration (across the app) or pass them to be named and used under a different name. Yet, you can still use your ES6 modules for anything that doesn’t fit the DI scheme. You also use have to inject directives for templating.
  4. Powerful templating that’s essentially HTML with sugar syntax that allows you to take advantage of two-way binding, events, etc. right in your templates. Built-in directives allow for even more powerful templating with foreach loops (for of), ngIf and others.
  5. Modular. Angular 2 is broken down to numerous sub-packages
  6. Angular Universal. A project that allows sharing Angular code between various platforms. There is work to make Angular work on the back-end and with NativeScript as part of app development.

All of what sounds just like Angular 1 but it’s just so much better. It’s modernized Angular.

But…why?

Did the evil JS men on Reddit or HackerNews tell you that Angular 2 is useless? Or that you might as well learn React? Well, you’ve been misinformed. First of all, jumping from Angular 1 to Angular 2 is magnitudes easier than jumping from Angular 1 to React (or Angular 1 and Ember). That’s the first thing to keep in mind. Second, many third party vendors are already working with Angular 2 to bring it wider support including NativeScript (kind of like ReactNative but it came out before RN) and Ionic (a framework that works with Cordova but uses Angular and Angular services/bindings for native features).

Let’s Build An App

As a preface, let me reiterate that Angular 2 is still evolving. One recent change that they made left most tutorials obsolete: they changed DI to use the keyword bindings instead of appInjector. And during the writing of this article, several other changes happened and I had to bump up a version of Angular to keep up to date halfway through. In fact, Angular went from alpha-43 to alpha-52 as I wrote it, and then it went to alpha-54 and finally to beta-0!

It’s not production ready. There aren’t enough people using it to vet it fully and the framework is still in alpha stage. On top of that, Stackoverflow is completely useless and documentation is lacking (was lacking even more when I wrote that sentence). That’s why this guide is called “preemptive”.

So I’d like to build an app with you, my readers, and kind of show off Angular from start to end. At the end of this tutorial, you should have a pretty good idea of how to build a sample Angular 2 application and how Angular 2 is the same and different from its predecessor.

So what will we build? A markdown editor!

Note: If you encounter any problems with the code, parts that are difficult to understand, or generally, have suggestions for the code itself, please file an issue on the Github repo for this project. That way I can help out and notify you when issues get fixed.

Step 1 – Basics

What you need to know

To continue with this tutorial, you’ll need to know some ES6 and you’ll also need to know how to use tools like gulp and npm. Basically, I want you to be a semi-modern front-end developer.

Setting up

Out of all the things I had to do to learn Angular, the most difficult part was setting up the environment. I decided to take that setup and turn it into an Angular 2 starter kit.

Go ahead and download it, remove .git and run git init to reinitialize the repo as your own and so you can throw it up on github wherever without the starter kit’s history tied in. Make sure to run npm install to install the project and run npm install -g gulp webpack typescript tsd to install all of the global requirements. You can optionally install bower via npm install -g bower which is used all the way at the end of the article for the optional Step 4, note that there will less handholding for that part.

Once you run gulp, the application will be accessible via localhost:3001 with livereload.

The setup basically creates a small angular app (that you can delete) and runs the compiler against it. It gets compiled from TypeScript to ES6 and then from ES6 to ES5. All of that is bundled up via WebPack. Lots of buzzwords, lots of “hot tech”, but the hard work has been done.

Next, to prepare for our exercise, delete the contents of app.component.ts in ./src/app and delete components and services from the app as well. Oh, and delete the contents from index.html while you’re at it. Also, make sure to delete boot.ts. After that, go to your gulpfile.js, your tsconfig.json, and webpack.config.json in the root of your project and change every instance of boot.ts to app.ts.

This way, we’ll get to start from scratch! 🙂

Bootstrapping

Rename app.component.ts to app.ts.This file was setup as the entry file for both webpack and TypeScript so it’ll be our bootstrapper. Let’s set it up:

//dependencies
import 'angular2/bundles/angular2-polyfills'; 

import { Component } from 'angular2/core';
import { bootstrap } from 'angular2/platform/browser';

These imports will make sure we can use ES6, various Angular 2 functionality and will also import from Angular what we’ll need to build our main component. Note the various Angular 2 subpaths. Angular 2 is VERY modular. It has polyfills included in its npm package but they have to be explicitly imported, it has a core (which contains truly core stuff), and bootstrap will bootstrap our application for browser use. There are some other platforms available: server, worker_app, and worker_render. If you want to see what other modules are available in general, checkout the source code.

Angular 2 doesn’t have any controllers or states that are setup that way. Everything is a component.

A component is basically like a directive except it’s closer to a React component than a directive. A component is a class that has several decorators attached to it to give it its extra features like the ability to create a view, use a template, inject dependencies, and have a selector (a custom HTML tag). It’s a self-contained unit that can also import outside components to use in its template.

And indeed, it doesn’t even have to have a view, the Component part is a decorator.. You’re basically attaching features to your class via these decorators.

The Angular 2 website likes to keep saying that your app is a “tree” with the various components being the nodes. It’s a great way to think about your app this way.

To continue our bootstrapping, we’ll have to create our umbrella component to hold our entire app (the super parent of our component tree) (which is a class with a bunch of decorators):

@Component({
  selector: 'markdown-app', //html selector <markdown-app></markdown-app>
  template: '<div>My app is running</div>' //can also specify templateUrl
})
class MarkdownAppComponent {
  constructor() {

  }
}

Note that Angular 2 also includes a View decorator

A decorator, for the unknowing, is basically something that wraps something else. A simple example is a log function that gets decorated with extra information:

function myLog(val) {
  console.log(val); //will console log out the pure value
}

function decoratedLog(log) {
  return function(val) {
    var now = new Date().toLocalTimeString().toString();
    log('[' + now + '] '  + val);
  };
}

var newLog = decoratedLog(myLog);

myLog('test'); //will yield 'test' in the console
newLog('test'); //will yield '[2:35:56 AM] test'

I hope that makes sense. TypeScript has built-in decorator support and JS is getting it with ES7. In our case, we can back up a bit, ignore implementation details and just remember the we can declare properties for our component by adding @Component({ }) right above it.

Now, we just need to let Angular 2 know that this component is our boostrapping component with this:

bootstrap(MarkdownAppComponent);

Note that class names have no play in the selector name. You can call your class Bamboo for all I care but it’ll still be available under its selector.

The selector associates a custom HTML element with the class. In our case: <markdown-app></markdown-app> will load our class wherever it is in our code.

Go to your index.html file and let’s make sure our app boostraps there too:

<html>
    <head>
        <title>Markdown App</title>
    </head>

    <body>
        <markdown-app></markdown-app> 
        <script src="bundle.js"></script>
    </body>
</html>

Congratulations! You’ve just created your first Angular 2 app! If you pull it up, it should say works and that’s it.

Components

Components are small units of logic that can have a view and it’s attached to a JS class. We’ve shown that with MarkdownAppComponent. Before we get into abstraction, I’d like to put some logic into our markdown app component, namely, I want it to have a real template.

Create a new file in ./src/app called markdownApp.html and we’ll use that to create our site. Note that since this article isn’t about CSS, I won’t be covering the crappy CSS I write to make this all work.

Let’s fill out our markdownApp.html template:

<div class="container">
    <div class="one-half col">
        <textarea>md goes here</textarea>
    </div>

    <div class="one-half col">
        html comes out here
    </div>
</div>

Next, let’s change our app.ts file and the View decorator:

@Component({
  selector: 'markdown-app',
  templateUrl: '/app/markdownApp.html'
})

Now we have our basic template in an html file stored away elsewhere. Let’s add some logic to our component real quick so that we can have our textarea be bound to a class variable and have HTML output.

The first thing you should know about is what’s called local variables, these are variables that exist solely in a View and cannot be accessed outside of the view. They’re pretty nifty and are pretty much the equivalent of having an ng-model in Angular 1 reference a scope variable that you never assigned in your controller.

First, we’ll have to make sure our textarea is bound to this local variable (which we’ll call rawMarkdown) like so:

<textarea #rawMarkdown></textarea>

Now, any change to the textarea will be reflected in the rawMarkdown variable. The variable, however, doesn’t just house the value, it has several different properties, it’s literally the DOM element object. Which means that to access its value, we can get it by doing rawMarkdown.value.

Then, we can reference this value by simply getting it out there with brace notation. The new template will now look like this:

<div class="container">
  <div class="one-half col">
    <textarea #rawMarkdown></textarea>
  </div>
  <div class="one-half col">
    {{ rawMarkdown.value }}
  </div>
</div>

But, this won’t work. We want to use rawMarkdown‘s value and convert that straight to markdown in our second column. The way to do that is to capture the keyup event on the textarea. This basically means that when a user stopped typing, run a function.

Unlike with Angular 1 where you had to do ng-click or ng-something, and hope that there’s a directive for your event (or have to hunt it down), Angular 2 takes a more HTML-compliant approach, it lets you capture the raw event like so:

<div class="container">
  <div class="one-half col">
    <textarea #rawMarkdown (keyup)="convertMarkdown(rawMarkdown.value)"></textarea>
  </div>
  <div class="one-half col">
    {{ rawMarkdown.value }}
  </div>
</div>

Whatever is between the parenthesis as an attribute will be the event captured.

Let’s backup a bit though, let’s go back to our class and change a couple of things, this is the final app.ts for this step:

/// <reference path="../typings/tsd.d.ts" />

import 'angular2/bundles/angular2-polyfills';

import { Component } from 'angular2/core';
import { bootstrap } from 'angular2/platform/browser';

@Component({
  selector: 'markdown-app',
  templateUrl: '/app/markdownApp.html'
})
class MarkdownAppComponent {
  public html = '';
  private md: any;

  constructor() {
  }

  public updateValue(val:string) {
    this.html = val;
  }
}

bootstrap(MarkdownAppComponent);

And as far as the template goes:

<div class="container">
  <div class="one-half col">
    <textarea #rawMarkdown (keyup)="updateValue(rawMarkdown.value)"></textarea>
  </div>
  <div class="one-half col">
    {{ html }}
  </div>
</div>

Note that we are using a public html variable which is accessible to us in the template, kind of like $scope variables are in Angular 1.

And now, whatever we write in our text field will show up next to it. But, a few things are off and we’re still not getting our markdown functionality.

How it looks:

Not much but it’s a start! You can view the compiled code here and the raw code here.

Step 2 – Services, events and markdown

Getting Markdown to work

This is less Angular relevant and more to do with ES6 imports. We can easily install a new module like so:

npm install marked --save
cd src/ && tsd install marked --save

The first line installs the marked library and the second line enables Typescript support.

We import it via:

import * as marked from 'marked';

Awesome, I’m going to store marked on the Class itself and use that stored instance to convert our value on updateValue.

@Component({
  selector: 'markdown-app',
  templateUrl: '/app/markdownApp.html'
})
class MarkdownAppComponent {
  public html: string;
  private md: MarkedStatic; //not sure why but that's the Marked type

  constructor() {
    this.html = '';
    this.md = marked;
  }

  public updateValue(val) {
    if(!val) { return ''; }
    this.html = this.md.parse(val);
  }
}

bootstrap(MarkdownAppComponent);

We could always encapsulate marked as a service but there’s no need for that right now. And we’re done! Right? NOPE! When you type in something like I'm done, you’ll literally see <p>I&#39;m done</p>. That’s not what we want for a preview.

Instead, we want to inject that html to be html rather than plaintext. In Angular 1.X, you’d have to go through the trouble of marking that text as secure or creating an scp filter. In fact, that’d be one of the first things you’d have to do when setting up an app.

To counter that, you can use a DOM property called innerHTML, but in the view like so:

<div class="one-half col">
  <div innerHtml="html"></div>
</div>

Yay! So now we have a working Markdown converter/editor. With a bit of styling, you could be done. But for our app, we definitely want to go a tad farther.

Services

Services in Angular 2 are different from services in Angular 1. For one, there is no distinction between a Factory, Provider, and Service anymore (am I forgetting anything?), there is simply a “service”. I’ve seen docs mention where you can initialize a service as a factory but never mind that right now.

The way to create a service is dead simple, especially when using ES6 or require. To put it simply, a Service is simply a class that has some configuration made to it and gets injected with that config into the app.

To put it another way, it’s the equivalent of importing a class into a file except you do it through Angular.

We’re going to create a service that’ll provide us with both Markdown (configured how we want it) and a LocalStorage component.

Create a folder in ./src/app/ called services and two files: localStorage.service.ts and markdown.service.ts. We’re going to start with the markdown service. Here’s basically what we want it to look like:

import * as marked from marked;

export class Markdown {
  private md: MarkedStatic;

  constructor() {
    this.md = marked;
  }
}

It’s not ready to be used by Angular just yet and I’d like to make some changes:

import * from marked;

interface IMarkdownConfig {
  sanitize?: boolean,
  gfm?: boolean,
  breaks?: boolean,
  smartypants?: boolean
};

export class Markdown {
  private md: MarkedStatic;

  constructor() {
    this.md = marked;
  }

  convert(markdown: string): string {
    if(!markdown) {
      return '';
    }
    return this.md.parse(markdown);
  }

  setConfig(config: IMarkdownConfig) {
    this.md = marked.setOptions(config);
  }
}

Phew, so there are a few TypeScript things here. First, there is interface. If you’re not aware of what an interface is, it’s a way to describe if objects are compatible with each other, meaning that if an object fulfills the requirements by an interface, it can be used in lieu of another object which fulfills the requirements of the interface.

In my eyes, and for the purposes that we’re using it here, it’s a way to describe a new “type”. In our case, we’re basically saying: “a config that goes into the constructor can have the values: sanitize, gfm, breaks, and smartypants”. The question mark after each property means that it’s optional.

What we’re trying to do is limit the number of options that can be passed to marked to the ones that exist and only the four that we listed. An application can send over an empty object and it’ll satisfy the requirements, it can also send over any number of those four options.

Note that we installed marked types with tsd so not only do we have our interface which another object has to satisfy to use it but marked has its own requirements. I tried to pass in an extra property to marked.setOptions and TypeScript threw an error.

Anyways, type safety. You’ll also notice that we created a convert method which accepts a string (markdown) and returns a string (html). Sweet.

Almost there, we have our functionality down, now we just need to make it compliant with Angular. There’s a decorator, similar to Component, which makes the class into an injectable service.

Add this import:

import { Injectable } from 'angular2/core';

and in front of the class declaration add:

@Injectable()
export class Markdown

Final result:

import { Injectable } from 'angular2/core';

import * as marked from 'marked';

interface IMarkdownConfig {
  sanitize?: boolean,
  gfm?: boolean,
  breaks?: boolean,
  smartypants?: boolean
}

@Injectable()
export class MarkdownService {
  private md: MarkedStatic;

  constructor() {
    this.md = marked.setOptions({});
  }

  setConfig(config: IMarkdownConfig) {
    this.md = marked.setOptions(config);
  }

  convert(markdown: string): string {
    if(!markdown) {
      return '';
    }
    return this.md.parse(markdown);
  }

}

This technically makes it a service!.

Next, let’s do our LocalStorage service. There’s nothing new here so I’ll just include all of the code:

import { Injectable } from 'angular2/core';

@Injectable()
export class LocalStorageService {
  private storage: any;

  constructor() {
    this.storage = localStorage;
  }

  public retrieve(key: string): any {
    var item = this.storage.getItem(key);

    if(item && item !== 'undefined') {
      return JSON.parse(this.storage.getItem(key));
    }

    return;
  }

  public store(key: string, value: any) {
    this.storage.setItem(key, JSON.stringify(value));
  }

}

Notice what comes after the function name: : type, this specifies a return type of a function. We purposely made the service abstract enough to use it for various keys.

Dependency Injection

Dependency injection in Angular 2 is a pain in the ass. Not because of its implementation but because of a lack of documentation on how to do it properly. I’ve so far seen four ways to inject dependencies:

  1. injecting viewBindings
  2. injecting bindings
  3. injecting providers
  4. injecting on app level on bootstrap

From what I understand, only the last one ensures shared state across the application. The others don’t. The last one also ensures that you don’t have to separately import a module to inject it. You simply reference the service in your class and it’ll be provided.

I honestly find it confusing which to use when as there can be an argument about a singleton/global service made for pretty much anything.

For instance, our Markdown service? That could be global. Or, it could also be component-specific as it holds a method that allows you to change the way it behaves. That could create all kinds of trouble.

But for clarity, we’ll inject Markdown using local injection since we might want to change its service settings. And LocalStorage globally as it is simply an adapter for the localStorage API that may do a few extra things.

Note that I expected this API to change as Angular 2 matures and the fact that every blog post out there about Angular 2 DI uses a different DI method supports that thought.

App-level DI

Let’s cover the last kind of DI first because that one makes the most sense for the time being.

We’re going to use it to inject the localStorageService into our application. Phew, it’ll probably be hard, right?

Go to your app.ts and add the following import:

import { LocalStorageService } from './services/localStorage.service';

Now comes the hard part, the last line of that file should be the application bootstrap, change it to this:

bootstrap(MarkdownAppComponent, [LocalStorageService]);

Wait, wait, wait. So that’s it? Yeah. That’s it. LocalStorageService is now available across the app. In fact, why don’t we use it in our MarkdownAppComponent? Surely, injection must be as cumbersome and confusing as it was in Angular 1, right?

Nope, let’s look at our constructor and our component:

class MarkdownAppComponent {
  public html: string;

  private localStorage: LocalStorageService;
  private storageKey: 'markdown-app';

  constructor(LocalStorageService: LocalStorageService) {
    this.localStorage = LocalStorageService;
    this.html = '';
  }

  public updateValue(val) {
    this.html = val;
  }
}

TypeScript creates certain metadata based off the constructor which Angular 2 picks up and uses as DI information. In this case, all that was necessary was just passing in alias: dependency where the alias is more often than not just the name of the dependency.

We’re storing it in a private localStorage variable. Also note that we stored our localStorage key. The cool thing is that TypeScript does type inferences so it already know that storageKey is supposed to be a string.

Let’s go ahead and create a method to store our markdown in our MarkdownAppComponent:

  public saveValue(val) {
    this.localStorage.store(this.storageKey, { text: val });
  }

We can also retrieve that value at construction time in our constructor:

var text = this.localStorage.retrieve(this.storageKey);
this.initVal = text ? text.text || '';

this.updateValue(this.initVal);

We can use this to populate both the original markdown and HTML. The HTML logic is already built-in so for the markdown, all you need to do is change the textarea like so:

<textarea #rawMarkdown (keyup)="updateValue(rawMarkdown.value)">{{ initVal }}</textarea>

Let’s look at the state of our app.ts currently:

/// <reference path="../typings/tsd.d.ts" />

import 'angular2/bundles/angular2-polyfills';

import { Component } from 'angular2/core';

import { bootstrap } from 'angular2/platform/browser';

import { MarkdownService } from './services/markdown.service';
import { LocalStorageService } from './services/localStorage.service';

@Component({
  selector: 'markdown-app',
  templateUrl: '/app/markdownApp.html'
})
class MarkdownAppComponent {
  public html: string;

  private localStorage: LocalStorageService;
  private storageKey: 'markdown-app';

  constructor(LocalStorageService: LocalStorageService) {
    this.localStorage = LocalStorageService;
    this.html = '';

    var text = this.localStorage.retrieve(this.storageKey);
    this.initVal = text ? text.text || '';

    this.updateValue(this.initVal);
  }

  public updateValue(val) {
    this.html = val;
  }

  public save(val) {
    this.localStorage.store(this.storageKey, { text: val });
  }
}

bootstrap(MarkdownAppComponent, [LocalStorageService]);

component-level DI

Component-level DI is similar but Angular will create a new instance of that service per component that injects it. This is at once useful and annoying. When you find it annoying, there’s always app-level DI! 🙂

The reason why component-level DI is useful is because you can change the service’s internal state in your component. Such as by passing it a config. We’re doing this for our MarkdownService. The service has a public method called setConfig which will affect the entire service. We don’t want various components overriding each other. Instead, we can set a configuration per component.

We’ll do just that for our MarkdownAppComponent. First, we need to add a appInjector property to the Component decorator:

@Component({
  bindings: [MarkdownService] //bindings was formerly appInjector
})

and then we reference that dependency in the constructor:

constructor(LocalStorageService: LocalStorageService, MarkdownService: MarkdownService) {
  this.localStorage = LocalStorageService;
  this.html = '';
  this.md = MarkdownService;

  var text = this.localStorage.retrieve(this.storageKey);
  this.initVal = text ? text.text || '';

  this.updateValue(this.initVal);
}

Here’s what everything looks like

You can view the compiled code here and the raw code here.

That should do it!

Step – 3

Adding more functionality

You can skip this part if you wish. It’ll just cover saving/deleting our data from local storage.

First, we need to add onto our MarkdownApp component. Let’s add a method called “saveMarkdown” and another called “deleteMarkdown”. We originally had a method called save but we’ll change that.

public saveMarkdown(val) {
  this.localStorage.store(this.storageKey, { text: val });
}

public deleteMarkdown() {
  this.localStorage.store(this.storageKey, {});
}

Next, we can reference these methods straight in our markdownApp.html template:

<textarea #rawMarkdown (keyup)="updateValue(rawMarkdown.value)">{{ initVal }}</textarea>
<br />
<a class="button" (click)="saveMarkdown(rawMarkdown.value)">Save</a>
<a class="button" (click)="deleteMarkdown()">Delete</a>

We’re using (click) to capture the event and delegate it to our methods. Now test it out and see if you can save/delete your markdown!

Subcomponents

Obviously, you can’t fit entire app into a single component. It’s time to separate stuff out. I definitely want the markdown textarea with all of its functionality be separate from the app’s implementation. I’ll get to why in a second.

First, create a new folder called components in ./src/app and editor folder inside of that. Inside of it, create a new file called editor.component.ts as well as a editor.component.html file.

We’ll move most of our logic there:

import { Component } from 'angular2/core';

import { MarkdownService } from '../../services/markdown.service';
import { LocalStorageService } from '../../services/localStorage.service';

@Component({
  selector: 'markdown-editor',
  bindings: [MarkdownService],
  templateUrl: '/app/components/editor/editor.component.html'
})
export class MarkdownEditorComponent {
  public html: string;
  public initVal: string;

  private md: MarkdownService;
  private localStorage: LocalStorageService;
  private storageKey: string = 'markdown-app';

  constructor(LocalStorageService: LocalStorageService, MarkdownService: MarkdownService) {
    this.localStorage = LocalStorageService;
    this.html = '';
    this.md = MarkdownService;

    var text = this.localStorage.retrieve(this.storageKey);
    this.initVal = text && text.text ? text.text : '';

    this.updateValue(this.initVal);
  }

  public updateValue(val) {
    this.html = this.md.convert(val);
  }

  public saveMarkdown(val) {
    this.localStorage.store(this.storageKey, { text: val });
  }

  public deleteMarkdown() {
    this.localStorage.store(this.storageKey, {});
  }
}

And move the entire markdownApp.html contents into our editor.component.html.

Let’s update our app.ts:

/// <reference path="../typings/tsd.d.ts" />

import 'angular2/bundles/angular2-polyfills';

import {Component} from 'angular2/core';

import { bootstrap } from 'angular2/platform/browser';

import { MarkdownEditorComponent } from './components/editor/editor.component';

import { LocalStorageService } from './services/localStorage.service';

@Component({
  selector: 'markdown-app',
  templateUrl: '/app/markdownApp.html',
  directives: [MarkdownEditorComponent]
})
class MarkdownAppComponent {

  constructor() {

  }
}

bootstrap(MarkdownAppComponent, [LocalStorageService]);

You’ll notice a few key changes. First of all, we’re not importing our Markdown service and that’s because we don’t need it as a global service and we’re not using it anymore.

Second, we added a new property to the Component decorator called directives, this is how you inject directives (such as ngIf) and components into a component. By injecting it, we can do the following with our markdownApp.html template:

<markdown-editor></markdown-editor>

And that’s it. If you run the app now, it’ll work just as before but now, we have a markdown-editor that’s its own component.

Passing information down the tree and built-in directives

Angular 2 has its set of built-in directives from for of to ng if and others. These are different from Angular 1 directives which included stuff like event listeners, class updates, etc.

I’d like to modify our app so that we can display any number of markdown editors, give them titles, and show those titles. (the titles won’t be editable…because).

First, let’s create a service that works on top of LocalStorageService and pulls the list of “posts” (markdown editor saves). We’ll call it PostService.

import { Injectable } from 'angular2/core';

import { LocalStorageService } from './localStorage.service'; //importing for type recognition

@Injectable()
export class PostService {
  private storage: any;
  private storageKey: string = 'markdown-app'; //hard-coded but there's no reason we can't extract it

  private posts: Object;

  constructor(LocalStorage: LocalStorageService) {
    this.storage = LocalStorage;
    this.posts = {};

    this.getPosts();
  }

  private checkPosts() {
    if(!this.posts) {
      this.getPosts();
    }
  }

  public getTitles() {
    var titles = [];

    this.checkPosts();

    for(let title in this.posts) {
      titles.push(title);
    }

    return titles;
  }

  public getPost(title) {
    this.checkPosts();

    return this.posts[title];
  }

  public getPosts() {
    this.posts = this.storage.retrieve(this.storageKey) || {};

    return this.posts;
  }

  public savePost(title, post) {
    this.checkPosts();

    this.posts[title] = post;

    this.storage.store(this.storageKey, this.posts);
  }

  public deletePost(title) {
    this.checkPosts();

    delete this.posts[title];

    this.storage.store(this.storageKey, this.posts);
  }

}

Phew, that was a lot but most of these are simple getters/setters so I won’t be going over it. Note that we can inject services into other services. Let’s next rewrite our editorComponent.

import { Component } from 'angular2/core';

import { MarkdownService } from '../../services/markdownService';
import { PostService } from '../../services/postService';

@Component({
  selector: 'markdown-editor',
  bindings: [MarkdownService],
  templateUrl: '/app/components/editor/editor.component.html'
})
export class MarkdownEditorComponent {
  public html: string;
  public initVal: string;

  private md: MarkdownService;
  private postService: PostService;

  constructor(PostService: PostService, MarkdownService: MarkdownService) {
    this.html = '';
    this.md = MarkdownService;
    this.postService = PostService; //using post service

    var text = this.postService.getPost('title'); //getting hard-coded "title" post
    this.initVal = text || '';

    this.updateValue(this.initVal);
  }

  public updateValue(val) {
    this.html = this.md.convert(val);
  }

  public savePost(val) {
    this.postService.savePost('title', val); //saving title post
  }

  public deletePost() {
    this.postService.deletePost('title'); //postService handles deletion logic
  }
}

Nothing crazy, we’re just hooking up to our new API and using title as a placeholder for a new post. (note that we don’t have a “add post” method but we don’t need it).

And the template needs its method names changed too:

    <a class="button" (click)="savePost(rawMarkdown.value)">Save</a>
    <a class="button" (click)="deletePost()">Delete</a>

And app.ts now:

/// <reference path="../typings/tsd.d.ts" />

import 'angular2/bundles/angular2-polyfills';

import { Component } from 'angular2/core';
import { bootstrap } from 'angular2/platform/browser';
import { MarkdownEditorComponent } from './components/editor/editor.component';

import { LocalStorageService } from './services/localStorage.service';
import { PostService } from './services/post.service'; //new global service, we could easily eliminate LocalStorageService from global usage

@Component({
  selector: 'markdown-app'
})
@View({
  templateUrl: '/app/markdownApp.html',
  directives: [MarkdownEditorComponent]
})
class MarkdownAppComponent {

  constructor() {

  }
}

bootstrap(MarkdownAppComponent, [LocalStorageService, PostService]);

Again, nothing crazy. We’re just injecting our PostService globablly. Sweet.

Now comes the fun part, creating multiple editors that work alongside each other. We’ll need to somehow pass in the key to our post from a parent component.

There are various ways of passing down information. In our case, we’ll use one-way data-binding (meaning that we pass down the prop value from parent to child, not the other way around). And that’s it. However, it’s pretty easy to do 2-way binding as well, check out an article on the topic.

Before we do that though, let’s render a list of those markdown editors from our list of posts.

In app.ts, import the ngFor module via the angular2/common submodule:

import { NgFor } from 'angular2/common';

And in our Component decorator, add the following to the directives listing:

directives: [NgFor, MarkdownEditorComponent]

This makes sure that the NgFor directive is available in our view. We’ll also get a title listing for our posts as well as a new method to “add post”

class MarkdownAppComponent {
  public titles: Array<string>;

  constructor(PostService: PostService) {
    this.titles = PostService.getTitles() || [];
  }

  public addPost(title: string) {
    this.titles.push(title);
  }
}

Next, let’s display our current “titles” in our markdownApp.html

 <div *ngFor="#title of titles">
  <markdown-editor [title]="title"></markdown-editor>
</div>

:/ Unfortunately, you’ll get the right listing of titles but everything will point back to our hardcoded title post.

I guess it’s time to look at passing data to props! In fact, we already started on it! That [title]="title" passes down a prop to our child component. Too bad the child component is not even aware that this happened…yet.

Let’s import a new decorator into our subcomponent called Input. Input, as I understand it, will do a one-way bind. It’ll update in your subcomponent whenever it updates in your main component.

To implement it in our subcomponent, we need two things. One, a @Input decorator that will consume the title variable and ngAFterContentChecked method which will initialize our data-fetching.

In your editorComponent, update the imports:

import { Component, Input } from 'angular2/core';

and in the class itself, add these two things:

@Input title: string;

ngAFterContentChecked() {}

We’ll move some stuff out of the constructor and put it in the ngAfterContentChecked like so:

ngAfterContentChecked() {
  var text = this.postService.getPost(this.title);
  this.initVal = text || '';
  this.updateValue(this.initVal);
}

When the component constructor runs, the title value is not yet available so we have to take advantage of one of the lifecycle hooks.

Cool, if you view your app now, you’d see a couple of editors (text from an earlier iteration of the app and title from another iteration). You can also delete which is exciting but deletion won’t actually remove it from the screen. That’s fine for now.

We’ll want to add new “posts” however. This is as easy as updating our app.ts with a addNew method and updating our html to use that method.

Add this to the MarkdownApp class in app.ts:

public addPost(title: string) {
  this.titles.push(title);
}

and a button to the markdownApp.html file:

<input #newPost type="text" /><a (click)="addPost(newPost.value)">Add</a>

Pretty simple, right?

Here’s what everything looks like

You can view the compiled code here and the raw code here.

Step – 4 – Last step

Can’t believe we’ve gotten this far. Let’s recap some of the things we’ve learned:

  1. Angular2 works REALLY well with TypeScript and ES6.
  2. Angular is component-based and components are really easy to make.
  3. Services are just classes that can get passed around the application as a singleton (or not).
  4. There are several types of dependency injection, depending on if you want a dependency from the app-wide pool or if you want a local dependency.
  5. Components have a lifecycle
  6. Angular still has one-way and two-way data binding.

At this point, I’m adding a bunch of CSS and HTML to make the app look a tad nicer. I’m hoping that if you made it this far, you won’t have trouble looking past the container and grid classes and div wrappers. I also reorganized some HTML in other ways but nothing, as far as functionality goes, works differently.

Routing

We obviously want to be able to use some kind of routing. In Angular2’s case, the built-in router will suffice. I wasn’t able to find any official documentation for the router so I had to piece things together from other places.

First, note that the router is for the time being included with the core but will most likely be ripped out once it’s done.

Let’s open up our app.ts and add a new import:

import { ROUTER_PROVIDERS, RouteConfig, RouteParams } from 'angular2/router';

The ROUTER_PROVIDERS is a service registration array. We can add it to our bootstrap to be able to inject them wherever we need them. One of those services is RouteParams which gives you the parameters for the current route.

RouteConfig is your configuration. It’s a decorator which wraps around your root component (MarkdownAppComponent) and sets up the routing. I find this part a little strange but hey, it works.

The routing is pretty simple actually. I don’t want to use our MarkdownAppComponent as our root component (since it has a very nice view attached to it). Instead, we’ll create a new class called App which will neatly wrap everything:

@Component({
  selector: 'app',
  templateUrl: '/app/app.html'
})
class App {
}

Change the boostrap declaration:

bootstrap(App, [LocalStorageService, PostService, ROUTER_PROVIDERS]);

One more thing we’ll do is move our MarkdownAppComponent into components/post-list/postList.component.ts because that makes more sense. We’ll use that component and its template as a way to display ALL of our saved posts. It’ll be our “home page”. Here’s what the component will look like:

import { Component } from 'angular2/core';
import { NgFor } from 'angular2/common';
import { ROUTER_DIRECTIVES } from 'angular2/router';

import { LocalStorageService } from '../../services/localStorage.service';
import { PostService } from '../../services/post.service';

@Component({
  selector: 'post-list',
  templateUrl: '/app/components/post-list/postList.component.html',
  directives: [ROUTER_DIRECTIVES, NgFor]
})
export class PostListComponent {
  public titles: Array<string>;

  constructor(PostService: PostService) {
    this.titles = PostService.getTitles() || [];
  }

  public addPost(title: string) {
    this.titles.push(title);
  }
}

I took away a lot of the superfluous logic and renamed the component. Afterall, it makes more sense to call it a “Post List” than a “Markdown App” now that it’s a sub-component. I copy/pasted the HTML into the postList.component.html file.

Let’s get back to app.ts. We’ll have to import our PostListComponent but we get to get rid of our MarkdownEditorComponent which is good news.

@RouteConfig([
  { path: '/', component: PostListComponent, as: 'PostList' },
  { path: '/post/:name', component: PostComponent, as: 'Post' }
])

The config goes in front of our App class. A few things about the config, here’s what it means:

  1. path that triggers a route.
  2. component that gets injected into the page where <router-outlet></router-outlet> component is. We’ll cover that next.
  3. as which creates an alias which we can use to link to that specific route.
  4. :var in the path works like any other router out there. Matches against the pattern, and pulls the info in as a route parameter.

Let’s create our app.html template which will allow us to show our route components. Before that, however, we have a few directives to import.

Add to your router import like so:

import { ROUTER_PROVIDERS, ROUTER_DIRECTIVES, RouteConfig, RouteParams } from 'angular2/router';

and update the Component decorator:

@Component({
  selector: 'app',
  templateUrl: '/app/app.html',
  directives: [ROUTER_DIRECTIVES]
})

Finally, let’s update the app.html:

<div>
  <div class="nav-container nav-main nav-right">
    <div class="nav">
      <a [routerLink]="['./PostList']" class="nav-title secondary">Markdown App</a>
      <ul>
        <li><input #newPost type="text" />
        <li>
          <a class="button secondary" (click)="addPost(newPost.value)">Add</a>
        </li>
      </ul>
    </div>
  </div>
  <div class="container">
    <router-outlet></router-outlet>
  </div>
</div>

We’re using a couple of new things. routerLink is the equivalent of ui-sref in the ui-router. And router-outlet is the equivalent of ng-view or ui-view. So we’re linking our app title to our post list which is incidentally also our homepage. We’re putting the view into our .container.

Lastly, we need to set the base href so that Angular can figure out how to structure its routes, we do that with a couple of lines of code.

Phew, let’s just put it all together:

/// <reference path="../typings/tsd.d.ts" />

import 'angular2/bundles/angular2-polyfills';

import { Component, View, provide } from 'angular2/core';
import { NgFor } from 'angular2/common';
import { bootstrap } from 'angular2/platform/browser';

import { APP_BASE_HREF, ROUTER_PROVIDERS, ROUTER_DIRECTIVES, RouteConfig, RouteParams, Location } from 'angular2/router';

import { LocalStorageService } from './services/localStorage.service';
import { PostService } from './services/post.service';

//pages
import { PostListComponent } from './components/post-list/postList.component';
import { PostComponent } from './components/post/post.component';

@Component({
  selector: 'app',
  templateUrl: '/app/app.html',
  directives: [ROUTER_DIRECTIVES]
})
@RouteConfig([
  { path: '/', component: PostListComponent, as: 'PostList' },
  { path: '/post/:name', component: PostComponent, as: 'Post' }
])
class App {
}

bootstrap(App, [LocalStorageService, PostService, ROUTER_PROVIDERS, provide(APP_BASE_HREF, { useValue: '/' })]);

Lots of imports which bothers me but other than that, not too shabby. Using provide, we’re able to inject our base href which is /.

Note that I commented out one of the routes.

Routing part II

Just to make things clearer. Let’s first update our PostListComponent’s html. Right now, it shows us TOO much. We just want it to show a list of links to our single posts and link to them.

To do that, we’ll have to import ROUTER_DIRECTIVES and inject them into our PostListComponent. Open it up and let’s get started:

import { ROUTER_DIRECTIVES } from 'angular2/router';

and injecting it in @Component:

directives: [ROUTER_DIRECTIVES, NgFor, MarkdownEditorComponent]

This should be second nature to you by now. And now, the view. Instead of using our markdown-editor selector, we’ll just link straight to our Post router (the one that’s commented out). Don’t forget to delete all references to the MarkdownEditorComponent since we won’t be using it here, that includes in the directives list above.

The ROUTER_DIRECTIVES declaration contains an array of router-specific directives, obviously. One of them is the aforementioned router-link. We’ll go ahead and use that to populate our list:

<div class="container">
  <div class="row">
    <div class="col-12">
      <div *ngFor="#title of titles">
        <a [routerLink]="['/Post', { name: title }]">{{ title }}</a>
      </div>
      <div class="row">
      </div>
    </div>
  </div>
</div>

Awesome. Note that we referred to the route as /Post unlike in the case of the link to PostList in the nav where we prepended a dot (./PostList). The dot basically tells us that the route is a child of the current route while just a slash will redirect us to the root component and its child routes.

This is important because it means that we can have routing configuration per component or per route at least to create sub-routes and sub-routing. Since the Post route was declared in our root component, we prepended a slash to its name and that’s it.

Since we commented out that route in our previous section, you’ll get an error. So before we move forward, we need to build our PostComponent.

By the way, this is what your postList.component.ts file should look like (and should be located in ./src/app/components/postList/:

import { Component } from 'angular2/core';
import { NgFor } from 'angular2/common';
import { ROUTER_DIRECTIVES } from 'angular2/router';

import { LocalStorageService } from '../../services/localStorage.service';
import { PostService } from '../../services/post.service';

@Component({
  selector: 'post-list',
  templateUrl: '/app/components/post-list/postList.component.html',
  directives: [ROUTER_DIRECTIVES, NgFor]
})
export class PostListComponent {
  public titles: Array<string>;

  constructor(PostService: PostService) {
    this.titles = PostService.getTitles() || [];
  }

  public addPost(title: string) {
    this.titles.push(title);
  }
}

Post Component

As always, create a new folder in components called Post with post.component.html and post.component.ts in it. Let’s open up post.component.ts and, you know what? I’m going to skip the boilerplate. Here’s the basic component:

import { Component } from 'angular2/core';

import { MarkdownEditorComponent } from '../editor/editor.component';

@Component({
  selector: 'post'
})
@View({
  templateUrl: '/app/components/post/post.component.html',
  directives: [MarkdownEditorComponent]
})
export class PostComponent {

  constructor() {
  }
}

Didn’t really need any internal logic. Well, the only thing we need is to retrieve the title from the URL so we’ll have to use a router component called routeParams.

First, import it:

import { RouteParams } from 'angular2/router';

Next, reference it in the constructor and save the value:

public title: string;

constructor(RouteParams: RouteParams) {
  this.title = RouteParams.get('name');
}

Since we have a working element, let’s use it in our app.ts router:

  { path: '/', component: PostListComponent, as: 'PostList' },
  { path: '/post/:name', component: PostComponent, as: 'Post' }

and import it up above, right under the PostListComponent:

import { PostComponent } from './components/post/post.component';

If you load up everything right now, you’ll be able to click into a post but it won’t show anything. What we want is to use our markdown editor. Luckily, all we need to do is pass this title into our markdown editor.

Let’s edit the postComponent.html:

<div>
  <markdown-editor [title]="title"></markdown-editor>
</div>

When you view the page now (if you have any titles posts added from the previous exercises), you’ll be able to navigate to the editor and back to home page.

Congratulations!

Here’s the PostComponent file so far:

import { Component } from 'angular2/core';

import { RouteParams } from 'angular2/router';

import { MarkdownEditorComponent } from '../editor/editor.component';

@Component({
  selector: 'post',
  templateUrl: '/app/components/post/post.component.html',
  directives: [MarkdownEditorComponent]
})
export class PostComponent {
  public title: string;

  constructor(routeParams: RouteParams) {
    this.title = <string> routeParams.get('name');
  }
}

Here’s what everything looks like

You can view the compiled code here and the raw code here.

Note If you can’t see anything up above, go back to Step 3 and add a few “posts” and save them. Reload the page and check the iframe again.

Where to now?

I’ve barely scratched the surface of Angular 2 but you should have an idea of what it’s like to develop in it by now and how it differs from Angular 1. I feel like many guides don’t stress the “app development” aspect enough or they do too much.

At the end of this tutorial, you’re on your way to writing a simple back-end for a blog or just a local markdown editor. Both of which are real-world applications. Is it simplistic right now? Yeah but you can expand on it.

I purposefully ommited a couple of implementation details from the app so that it’s not fully finished as an exercise for you. I’ll probably have those working on the github repo some time after the article is released.

If you want the exercise, the first thing to look at is adding new posts from the step-4 architecture. So you’ll have to figure out a few things for that:

  1. adding a post and having the homepage listing update with each addition. This can be handled with a pub/sub system, with Rx streams (or rather observables so basically using 2-way binding), or even by using a Flux-like architecture
  2. separating the “addition” module into its own component…or not?
  3. programmatically redirecting a user to the new post after submission.

Also, there is a type error when it comes to retrieving parameters, can you think of a way to preven that? (hint: read the docs).

If you’re interested in further development of this application, star the repo. I’ll be releasing updates as I use it. There won’t be any new steps, however.

Further learning

Some extra resources: