- Read Tutorial
- Watch Guide Video
In this guide we're going to continue building out our service and we're going to import some libraries that are going to allow us to connect outside API's. And then we're going to actually build the function that is going to go out and get our documents. So to start off let's pull in the libraries that we're going to need. We're going to need five different libraries.(in app/documents/document.service.ts
) The first one is going to be Http
. It shouldn't all be capitalized just the first letter. Also Response
. And then we also need Headers
and then RequestOptions
; and these are all going to be found in '@angular/http'
.
import { Http, Response, Headers, RequestOptions } from '@angular/http';`
And as you can see, when I finish typing, all of our errors are going to go away. So those are the first four. Now the other one that we need is called the Observable
library. Now observables in angular and not just in angular but in Javascript in general are some of the most powerful but also some of the most challenging items to learn that you're probably going to come across. So let me finish typing in this import statement and that's where the library is, it's in rxjs/Observable
import { Observable } from 'rxjs/Observable';
Now let me just take a step back and talk about observables and to be completely honest an entire course could be built just on observables so I'll do my best to drill it down to the most basic level so that you know the most important thing which, is how to use it. First, we'll go to the library it comes from which is rxjs
. So, rxjs
(and I'm going to save this). And let's actually pull this up in the browser because this is worth it. And also because I want you not only to follow this guide but I want you to be able to research this so if I type in rxjs
. This is going to talk about reactive development. So when you hear react you may think the react javascript framework and that is a framework. However, I'm not talking about the react framework. Instead what I'm talking about is actually a type of programming and it's called reactive programming. So this is a very different way of thinking about development. So you can go to reactivex.io.
And this is a set of javascript libraries that follow something called the Observer pattern. So observers give you the ability to set up streams in your application. And this is one of my favorite diagrams for how observables work.
So imagine a Rails app. This is the most basic way of getting a base case with a Rails app you don't have the concept of an observable. Instead, you just have different actions that a user can take. So if you go to Twitter and you type in a tweet and hit send then it's going to be sent and posted and then it's going to show up, and all of it's very sequential. What observables do is they actually set up streams of data. So what you can do is not something as linear as going to a website clicking on something and then having the application perform some action because you as a user took that action. Instead what observables do is they allow multiple things to happen at the same time or at least at structured intervals. So right here this is the most common diagram that you're going to see for observables and you could imagine this line right here being some type of action that gets taken.
So imagine our documents right here with our documents you could imagine that this is a user who goes to the document component on the site. Now the first thing that may happen is right here is when the page loads we want the API to be called and we want all of the documents to be brought back. However, that is not the end of the lifecycle. So with the way the observer works is if we go down the line right here we may have intervals that we call the API in the background. So right here we may set up structured intervals and we are going to do that where every five seconds we call the API and any new documents that have been added they will be returned and we won't even have to make a page refresh. So what's going to happen here is that a structured interval is going to happen at this node and it's just going to keep on happening. Now we also could do something like put in a refresh data button. So if you imagine that this blue node is a structured timed interval. In this light green one is a structured time interval, this dark green one could be one where we pressed a refresh button and we didn't refresh the page we just refreshed the API. So you had starting here on the purple side you had the page loading then you had our first interval. So it got called in the background. Then a user pressed a refresh button and then the page or the component called the service again. And that just happens over and over again. And then you also have the ability to even change state. So if something pops up and you edit an item or do something like that then it would come here into this second level and then you might have another set of intervals or you might have some other rules that apply. So that is a very high-level version of what the observer pattern is it's where you set up streams and you can think of a user going to a page of your application. Don't think of it just as going to the application but think of it as going to something that has an entire lifecycle all to itself. And that's the way the observer pattern is going to work.
So with all of that being said, now that you know kind of a high level on what observables are we need to actually implement it and because we're implementing a document we need to import that as well. So I'm going to say
import { Document } from './document';
And then inside of our Injectable
. This is where we're going to place our API connector. So starting off we need to have our API URL. So I'm going to say
@Injectable() export class DocumentService { private documentsUrl = 'http://localhost:3001/freelance_documents.json'; }
We want to set the documentsUrl
equal to whatever the URL query is. So in our case, it's the URL where the JSON is being called. localhost:3001/freelance_documents
So I'm going to copy this and paste it in. And I'm also going at the very end I'm going to append .json
just because I usually like to put the data type there at the very end and if I want I can also paste .json
in(to the browser URL) and you can see that nothing changes. The same thing happens on both sides.
So that by itself isn't going to make the API call, all this is going to do is it's going to store this in the variable and then we'll call this later on. Now the next thing we're going to create is a constructor. So what a constructor is going to do is it is going to create our connection with Http
. So inside of this we're going to use dependency injection and remember dependency injection is just like what it sounds like it is injecting a dependency and in this case, our dependency is going to be Http
. So, I'm going to say private http: Http
and then bring in the Http
type and we're not going to have any actual implementations so we'll just have empty curly brackets and this is a standard convention for performing dependency injection in angular 2 applications.
constructor( private http: Http ) {}
So what we're saying is that we have this Http
library and we need to call it as soon as the class is instantiated and that's a reason why we have this in our constructor.
So now that we have that we're ready to create our function that is going to go and get the documents. And I think a good name for this will be getDocuments()
. It's going to be a function.
So it's just going to be parentheses we're not going to put anything inside of it. And what we're going to return here this is going to be a little bit different because we're going to return an observable, an observable takes a type argument in the way you give it its type argument is through angle brackets. So we want this observable to return an array of documents. And the way that we declare that remember is <Document>
which we just imported and then we want an array.
getDocuments(): Observable<Document[]>
So we have the brackets right there. And inside of this function, this is where all the magic is going to happen. So this is where we are going to connect to the API and then we're going to send the request and then get the response. So here I'm going to say
getDocuments(): Observable<Document[]> { return this.http.get(this.documentsUrl) }
By doing this what we're doing is we're creating a singleton method which means that we're going to connect a single connection to this API that we can call at any time and bring back the data. http
is what we made available inside of the constructor. .get
is a function provided by Http
, so it allows us to go get the data from an API and I'm going say this.documentsUrl
. So all this is doing is it is saying that I want you to create an instance of Http
and I want you to go get the contents of the API which can be found at this URL endpoint and that's all that we want to do on that side.
And now, what we're going to do is we need to tell it what to do with the data. So I'm going to say (the syntax for this is a little bit different). Technically I could put all this on one line but then it gets really long and it's very hard to read. So the standard convention is actually when you chain functions together like we're going to do, is to line them up.
So the next thing that we're going to pass in because if you notice we got an error message and the error message was because just getting the contents of the URL doesn't do anything we actually have to do something with what our response says. And in this case we're going to use the .map
function now .map
is something that you are going to see constantly when working with observables and what it's going to do is it is going to map a response that we can actually work with. So if I come in here I'm going to add parentheses and then I'm going to also create a variable so I'm going to say response
. And I'm going to say this is of type Response
.map((response: Response))
So this is a very specific type of response and that's the reason why we pulled it in from this library up here.
What this is going to allow us to do is connect to the API and then because it's a data type Response
, angular is going to know how to handle this more than if we didn't supply the data type. So here I'm going to use a fat arrow. So we're going to use an anonymous function here. And what we're saying is we want the response to be mapped to JSON data of type document. Now that doesn't make sense. Don't worry that this part is like I said one of the most challenging things about this entire course. And so we're going to say this is of data type document. It's going to be the response and then we're going to convert this data to JSON.
And so what this is going to do is give us our response and then this is mapped because we're using the map method directly to the data type of document. Which, is going to handle the response. So all it's essentially doing is saying bring me back the response and then convert this all into JSON data with one of the biggest keys being that the JSON data has to be mapped to the document. So this is why having the document interface is so important because what it's going to do is map that data from the response so that it looks like the document interface. So if you open up the document interface which is in app/documents/document.ts
and then you open up the JSON data you can see here that we have items such as a title which, is going to be mapped to title and then a description which is going to go to description and so on and so forth and this is part of the reason if you remember earlier when I said that this is the reason why I named my variables with snake case instead of camel case it was because I wanted to have a very easy transition between the data coming in so that it could be mapped directly to these values. And so that is what we're going to do here. Now we do have one little error where it says property map does not exist on type observable. And I think that's just based off of where we're importing observable from. So I think that if I ( go to app/documents/document.service.ts
) and import observable from rxjs/Rx
like this...
import { Observable } from 'rxjs/RX';
then, save now this is working. So this is fixed.
So that is the function that we are going to use. And we also will want to handle errors. And so the last thing I do in this guide before we move on to getting these to pop up over here is let's just add some error handling. And so the way that we're going to do that is I'm going to bring in the actual error handling code provided by angular because as you will see this is not something that you probably would want to watch me type. I'm going to past this in here and this is available in the angular documentation it's called handleError
.
private handleError (error: Response | any) { // In a real world app, we might use a remote logging infrastructure let errMsg: string; if (error instanceof Response) { const body = error.json() || ''; const err = body.error || JSON.stringify(body); errMsg = `${error.status} - ${error.statusText || ''} ${err}`; } else { errMsg = error.message ? error.message : error.toString(); } console.error(errMsg); return Observable.throw(errMsg); }
And what it's going to do is it's just going to take the response, if it's an error then it's going to just check for the body type and it's going to give us all the information we need and then output an error. So you want to have this because imagine what would happen if the servers down for the micro service or there's some kind of error that's occurring on that side. You want to have some detailed explanations on what is happening and why the error occurred so that you can go fix them. And so now all we have to do is we just have to call this. So I'm going to do
.catch(this.handleError);
So what this is going to do is it's going to getDocuments
then it's going to grab the http
value it's going to do all of its magic right here.
This is where it is all really happening. This goes and gets the data but the data by itself doesn't do us any good. We need the ability to convert the data into something that our application can actually use. And in this case it's this document interface. And then lastly we chain on .catch
. Just so in case any errors occur then we're able to keep those and we could eventually put them in a logger or we could change up our behavior on the site and put a different kind of error page. So that was a very long guide. And the biggest part of it being kind of an introduction to what observables are and now we have implemented this. And so we've implemented our get documents function and in the next guide we're going to actually wire this up into our documents.component.ts
so that we can start pulling these in and showing them on the screen.