Let’s cut to the chase: they are gone. And in my humble opionion that’s a good thing. The concepts of factories, services, constants, values and providers in Angular 1.x have been critized a lot and caused a lot of confusion for people new to Angular. Even after four years of Angular development, I’m not always sure which one to choose. Also why are constants called constants when they do not act like a constant. Ugh.

Fortunately things get a lot easier in Angular 2. Although it is still in alpha (I’m using 2.0.0-alpha.26 for this post) it is already starting to look pretty awesome.

A simple service in Angular 2

Let’s create a service to find photos at Flickr. A service in Angular 2 is simply a ES6 class.

class Flickr {
  }
  

We do not need to extend any built-in class or add annotation, this is it.

The $http service also dissapeared, and there is no replacement (yet, but there will be). Fortunately there are some great http utilities out there (like Superagent), and we could even start to use the Fetch API. Let’s use fetch for simplicity sake.

class Flickr {
    searchPhotos(query) {
      return fetch(`http://api.flickr.com/services/rest/?&method=flickr.photos.search&api_key=[your api key here]&texts=${query}&format=json`).then(function(response) {
        return response;
      });
    }
  }
  

All this does is getting some photos by a given query and returns the data as JSON.

So, how do we use this Flickr class in a application? Luckily the Angular team did not drop dependency injection, as it was one of the greatest features in Angular 1.x. Let’s inject our Flickr class into our component:

// Assuming the class exists in a different file
  import {Flickr} from 'flickr';

  @Component({
    selector: 'my-photos-component',
    appInjector: [Flickr]
  })
  

Then in the constructor of that component we simply inject it:

constructor(flickr:Flickr) {
    this.flickr = flickr;
  }
  

This beautiful syntax is made possible by TypeScript. Every time the application runs into a variable of type Flickr, it will create an instance of the Flickr class for us. Please note that this is a singleton.

Now we have a Flickr instance injected and saved in our component, let’s see how we can use this instance to get some photos:

searchPhotos(query) {
    this.flickr.searchPhotos(query).then((photos) => {
      this.photos = photos;
    });
  }
  

That will call our service and assign the photos to this.photos. Did you see we do not even have to call $apply anymore, even though fetch is not part of Angular? Mindblowing.

We still need to call the searchPhotos method from the DOM:

<input type="search" #photoQuery />
  <button (click)="searchPhotos(photoQuery.value)">Search photos</button>
  

One last thing! Of course we want to show the results to our users:

<ul>
    <li *ng-for="#photo of photos">
      <img [src]="createFlickrImagePath(photo.id, photo.owner)" width="250px">
    </li>
  </ul>
  

The ng-for is a new directive in Angular 2 and it replaced ng-repeat. Also the * in front of it is part of Angular’s new template syntax. In order for the ngFor – and every other directive – to work, you will need to import it and add it to the directives array of your component before it works.

Our complete component now looks like this:

import {
    ComponentAnnotation as Component,
    ViewAnnotation as View,
    NgFor
  } from 'angular2/angular2';

  import {Flickr} from 'flickr';

  @Component({
    selector: 'my-photos-component',
    appInjector: [Flickr]
  })
  @View({
    template: `
    <input type="search" #photoQuery />
    <button (click)="searchPhotos(photoQuery.value)">Search photos</button>
    <ul>
      <li *ng-for="#photo of photos">
        <img [src]="createFlickrImagePath(photo.id, photo.owner)" width="250px">
      </li>
    </ul>
    `,
    directives: [NgFor]
  })

  export class photosView {

    constructor(flickr:Flickr) {
      this.flickr = flickr;
    }

    searchPhotos(query) {
      this.flickr.searchPhotos(query).then((photos) => {
        this.photos = photos;
      });
    }
  }
  

Please note that you will need a ES6 transpiler like Babel to run this example.

Leave a Reply