Programming

This weekend, I decided to give Angular 4.0 a try. I was very happy when I managed to create a rudimentary Reddit client with it in less than 15 minutes. Yes, I was able to effortlessly create a new component, a new service that can asynchronously fetch and process Reddit’s JSON data, and use both together in my app. In this tutorial, I show you how I did that.

Prerequisites

To avoid weird errors, I suggest you use recent versions of both node and npm. I used npm v4.1.2, and node v7.7.4.

Install Angular CLI

Without the CLI, working with Angular 4 is extremely hard. Therefore, the first thing you do is install it using npm.

npm install -g @angular/cli

Create a New Project

We can now use the CLI to create a fully-configured Angular 4 project.

ng new my-reddit-app

The above command might take several minutes to complete. The resulting project will be about 198 MB. Yes, that’s a lot of bytes for a blank project, but let’s ignore that detail for now.

Enter the project’s directory and run the app to make sure that it is configured correctly.

cd my-reddit-app ng serve --open

If everything’s okay, you should see a new browser window open and display a page with a short message.

Create a New Component

Let us now create a new component that lists the hot posts from a specified subreddit. A component, as you might already know, usually has its own directory containing an HTML file, a TypeScript file, and a CSS file.

Using the CLI, you can create all those files with a single command:

ng generate component my-reddit

The generated component is ready to be used, without any configuration changes. So, go to src/app/app.component.html and add the following line to it:

<app-my-reddit subreddit= "/r/askreddit" ></app-my-reddit>

As you can see, by default, the component’s tag will have an app- prefix. It also has an an attribute called subreddit , specifying the name of the subreddit it should display.

Additionally, give a more appropriate title to the page by opening app.component.ts, and changing the value of the title field.

export class AppComponent { title = 'My Reddit Client' ; }

Your page should now look like this:

To be able to receive the value of the subreddit attribute in your TypeScript code, add a field to the MyRedditComponent class using the @Input annotation.

@ Input ( 'subreddit' ) subreddit : string ;

Define a Reddit Post

Our rudimentary Reddit client will simply display the titles of posts, along with URLs. Therefore, let us now create a TypeScript class that can store the two values.

Create a new file called post.ts and add the following code to it:

export class Post { title : string ; url : string ; }

Create a New Service

To fetch data from Reddit’s servers asynchronously, we need a service. To create one, you can use the CLI.

ng generate service my - data

You can now open my-data.service.ts and start adding your code to it. First, import the Jsonp class because we’ll be using it to fetch Reddit’s data. We’ll also need the Observable class for the asynchronous processing, and map to process the result of the observable. Lastly, import the Post class.

import { Jsonp } from '@angular/http' ; import { Observable } from 'rxjs/Observable' ; import 'rxjs/add/operator/map' ; import { Post } from './post' ;

Next, you can use dependency injection to initialize a Jsonp instance.

constructor ( private jsonp : Jsonp ) { }

To actually fetch the data, create a new method called fetchPosts() . Let it take the name of a subreddit as an argument, and let its return value be an Observable of type Post[] .

fetchPosts ( subreddit : string ) : Observable < Post [] > { }

Inside the method, you can call the get() method of the Jsonp instance and pass a Reddit API URL to it. The URL must, of course, include the subreddit. It must also have a jsonp parameter whose value is set to JSONP_CALLBACK .

You can attach the map() function to the output of get() and parse the JSON data returned by Reddit’s servers. For now, we’ll just loop through the children array of the JSON data, and extract the post titles and URLs. We’ll, obviously, use those to populate Post instances. Accordingly, add the following code:

return this . jsonp . get ( "https://www.reddit.com" + subreddit + "/.json?jsonp=JSONP_CALLBACK" ). map ( data => { var posts : Post [] = []; let children = data . json (). data . children ; for ( var i = 0 ; i < children . length ; i ++ ) { let post : Post = new Post (); post . title = children [ i ]. data . title ; post . url = children [ i ]. data . url ; posts . push ( post ); } return posts ; });

Note that we can use the json() function to convert the JSON string to a JS object.

Our service is now ready. However, it can’t be used until you also add JsonpModule to your app module’s imports. Therefore, open app.module.ts , and inside the imports array, add JsonpModule . After doing so, the array should look like this:

imports : [ BrowserModule , FormsModule , HttpModule , JsonpModule ],

Use the Service

We’ll be using the service inside the my-reddit component. Therefore, open my-reddit.component.ts and import the service, along with the Post class.

import { MyDataService } from '../my-data.service' ; import { Post } from '../post' ;

Include MyDataService as one of the providers of the component. Additionally, use dependency injection to initialize an instance of the service. Also add an array of Post objects as a member variable of the class. After all those changes, your class should look like this:

@ Component ({ selector : 'app-my-reddit' , templateUrl : './my-reddit.component.html' , styleUrls : [ './my-reddit.component.css' ], providers : [ MyDataService ] }) export class MyRedditComponent implements OnInit { @ Input ( 'subreddit' ) subreddit : string ; posts : Post [] ; constructor ( private data : MyDataService ) {} ngOnInit () { } }

Inside the ngOnInit() method, just add a call to the fetchPosts() method, subscribe to its observable, and assign its return value to the posts member variable.

this . data . fetchPosts ( this . subreddit ). subscribe ( posts => this . posts = posts );

Finally, modify the HTML file associated with the component to actually display the contents of the posts variable. Make sure you use the *ngFor directive to loop through the posts.

<h3> Posts from {{ subreddit }} </h3> <ul> <li * ngFor= "let post of posts" > <a href= "{{ post.url }}" > {{ post.title }} </a> </li> </ul>

If you go to your browser now, you should be able to see the posts from Reddit.

Conclusion

You now know how to use Angular 4. It was easier than you expected, wasn’t it?