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.
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.
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
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.
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;
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;
}
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
],
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.
You now know how to use Angular 4. It was easier than you expected, wasn’t it?