Using an Angular 2 Observable to Stream Live Data for the Proposal List Component
This guide reviews how to integrate an Observable in Angular 2 in order to stream live data in the Proposal list component.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

Now that we're back in our angular application it is time to start building out all of the components that we're going to need. So if I come in the browser and navigate to localhost:3000/proposals what we want is for the JSON data to be populating this proposal list instead of our hard-coded data. So switching back to Sublime we're going to follow a very similar pattern so I'm going to go through these next few steps a little bit faster than last time just because I'm not going to completely repeat exactly what I said before and the process that we're going to implement for proposal is going to start off very similar at least when it comes to the proposal list. Then we're going to branch off into something completely different when we start getting into the proposal show. And then the proposal new components. So I'm going to start off in our freelance-camp-fe/app/styles/app.module.ts and we're going to create a proposal service. So here we have all of our proposal components and right below it. I'm just going to create a ProposalService. And this is going to be coming in from ./proposal/proposal.service which we haven't created yet but that's perfectly fine.

import { ProposalService } from './proposal/proposal.service';

large

Now we're going to add ProposalService to our list of providers.

providers: [
  DocumentService,
  ProposalService
],

medium

Hit Save. And this is going to throw an error and break it because we don't have this file yet. So let's copy proposal.service and in the proposal directory, create a new file and hit save and name it proposal.service.ts Now inside of this, we can use our document.service.ts as a model. So right here you can see all of the things we have in here. We're not going to worry about all of it. And for the most part, we should be able to just copy and paste most of this inside of it. We are going to be using injectable. We're going to be using each one of these items (inside document.service.ts) we're going to be using an observable except right here we're going to be using a Proposal instead and this is going to be coming from ./proposal.

import { Proposal } from './proposal';

large

Now you always have to be careful whenever you copy and paste an entire file over. So I'm just saying that as a warning if we run into any bugs it probably is because of that but I don't want to waste your time with just watching me type a bunch of things especially when it's nearly identical to what we have in document.service.ts. Your time is incredibly important to me and I want to spend it teaching you things that are unique that we haven't already covered. So we have ProposalService, we're going to have a proposalsUrl and notice I say proposals here because in my mind this is a plural kind of service so we have proposalsUrl and then we have this which is just going to be 'http://localhost:3002/proposals.json'.

private proposalsUrl = 'http://localhost:3002/proposals.json'

large

And you can test this out ( in the browser ) by just coming here and you can just copy and paste 'http://localhost:3002/proposals.json if you would prefer. And you can add.json to the end or leave it off it doesn't really matter. Both of them are going to work. So now that we have all of this our constructor is going to stay identical. And instead of saying getDocuments now we're going to say getProposals and our observable is going to take in a <Proposal[]>. So it's going to be an array of type Proposal and instead of documentsUrl it's going to be proposalsUrl. And instead of <Document[]> array right here it's going to be a <Proposal[]>. And that is all of it.

getProposals(): Observable<Proposal[]> {
  return this.http.get(this.proposalsUrl)
                            .map((response: Response) => <Proposal[]>response.json())
                            .catch(this.handleError);
}

large

So that is everything that we need for our service. I told you it was going to be a lot faster this time mainly because we didn't have to have the whole lecture on what an observable is and everything like that. So now that we have that and we've imported all of these items now we can go look at what we need to do to our proposal-list.component.ts and if your first thought is to go look at and see what we did on our documents component then you are thinking like a developer. So I'm going to go to my documents.component.ts. It'd be awesome if we could have all of this memorized. However if this is the first few times you've done it it is perfectly fine to reference the other things that you've done. So here we have documents.component.ts Now we need to open up our proposal-list.component.ts and this is what we want. This is one where we created all of these various items and that's how we're calling proposals which is fine here but we don't actually care about this anymore. Now we can get rid of these so I'm going to get rid of all of this.

large

And I'm also going to get rid of these three calls.

large

If I hit save. This is now just going to be an empty list

export class ProposalListComponent {
  proposals: Proposal[];
}

And we dont have any errors so that's good. So now that we have this what else do we need to bring in? Remember that we need to bring in OnInit. So this top line we need to have OnInit and then we also need to have Observable. And then we need to have our ProposalService so not DocumentService we need to have ProposalService. And this is going to be './proposal.service.

import { Component , OnInit } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { Proposal } from './proposal';
import { ProposalService } from './proposal.service';

large

So all of this is working so we're good to go on this side. Now we need to add a provider. So here I'm going to add a component and inside of our component metadata I'm going to say providers: and pass in the provider of [ProposalService].

providers: [ProposalService]

large

So everything there is good to go. Now we can use it now with ProposalListComponent. We want this to implement OnInit, which means that we're going to have to create an OnInit function inside which is fine. And then here we want to create some variable. So the first one we're going to create is anerrorMessage: and then this is going to be of type string. And then we also want to declare the modes so this mode is going to be set to "Observable" and that is it.

export class ProposalListComponent implements OnInit {
  proposals: Proposal[];
  errorMessage: string;
  mode = "Observable";

large

so now we can move down to the constructor. Now our constructor is pretty basic. Remember we're only passing in and we're only using dependency injection that's all we're using our constructor for. And here I'm going to go inside the constructor I'm going to call the document or in this case, I'm going to call proposalsService(plural) since we're bringing in multiple you could actually do it either way because with documents I uses the singular one but you can do it whichever way makes the most sense to you. So ProposalService is going to replace DocumentService. And now that's everything that you need for dependency injection.

constructor(
  private proposalService: ProposalService
)  {}

So we're moving right along and hopefully, you're appreciating that we're going a little bit faster than last time so that we can get to see things actually pop up on the screen. Next thing is our ngOnInit() and I'm just going to copy it from documents.component.ts because this is going to be very similar. The only difference is instead of getDocuments it's going to be getProposals

ngOnInit() {
  let timer = Observable.timer(0, 5000);
  timer.subscribe(() => this.getProposals());
}

and it's complaining because we don't have any proposals which is totally fine. That is what we're going to be setting up here. And now with getProposals() we're going to declare it here and replace getDocuments with getProposal and we want our proposalService to replace documentService the last thing is to replace this.documents with this.proposals. And then we want to change the variable documents. Now technically this would still work because documents here is just a variable argument but still it would be a very bad thing to keep that in there just because it would look bad.

getProposals() {
  this.proposalService.getProposals()
        .subscribe(
          proposals => this.proposals = proposals,
          error => this.errorMessage = <any>error
        );
}

So now I'm going to save and look at that ( the browser ). Look how much easier that was the second time through. So we're able to create our proposals and are full proposal-list.component.ts just in a couple of minutes and now we have the ability to have this data and this is our data coming straight in from our API and we can test this out by opening up a terminal session.

rails c

And I'm going to create a new proposal here and this one is going to take a little bit of time to do. So let me actually open up and do this in code just so it's a little bit easier and a little bit faster. So here I'm just going to take this and in the seeds.rb file. And lets say "Google" for "customer: we got lucky. So here I have a customer. Make sure to get rid of the tabs or else you're going to end up with some nasty error messages in the terminal. And also make sure. Notice how our seeds.rb file worked and we have different values for each one of these. I think that is also pretty cool. So let's change our estimated_hours: to 120 it's going to be a big project. and that should be everything that we need.

Proposal.create!(customer: "Google", portfolio_url: 'http://portfolio.jordanhudgens.com, tools: 'Ruby on Rails, Angular 2, and Postgres, estimated_hours: 120, hourly_rate: 120, weeks_to_complete: 12, client_email: 'jordan@devcamp.com')

So let me try running this. So that added and look at that. We now have Google and that all got added dynamically because we set up our observable there. So if you followed through that great job hopefully it's starting to kind of make a little bit more sense because the last time we went through that I really took my time going slow and methodically through it.

But now you can see that this is relatively straightforward we're creating an API connector and then we're mapping the data that comes in with JSON data and then we pass it to the component. And another thing, I'm not sure if I mentioned it last time that I find really cool about this is notice how we didn't touch the HTML code.

I mean that's something that really is important. That's something I really want to stress through this course is that if you are having to make changes in the template file simply based on what the API is returning. If it's anything more than just some small formatting things then you're probably setting up your code wrong. But when you can make changes and even sometimes significant changes like changing the entire API provider just like we did then this is a sign that you're building your microservice the right way. So a good example of this would be we started with sample data and this was just sample data that was pulled in right here and we stored it in the component. And that's what we iterated over and that's perfectly fine.

But let's imagine that you initially were connecting to a Rails application but as time goes on some things change and someone new comes in and they want to change that microservice so that it's something with a no-SQL database and you're going to connect directly to firebase to have a direct connection to a no-SQL kind of data store. With this kind of setup, we could have made all of those changes.

Now that we have a service we could make all of those changes we could change the API connector right here and make everything just change just from the data source and then the entire application would still work perfectly. Assuming obviously that the parameters are the same that the parameter names are the same. But if they are then you could just simply change the URL and pull it in from a completely other application or a different API that's applying the same kind of data. That's something that is incredibly powerful when it comes to building and scaling up applications because let's say that our proposal service starts out as kind of a prototype application something very basic and which is exactly what it is we built it in a few minutes. So say that it starts out like that but eventually it turns into a huge system and it turns into a number of micro-services that all communicate with each other and then they have one API endpoint that you can communicate with. Our front-end application will not even care about all the changes that the proposal made. All that matters is as long as the data that comes back still has the same attributes so if the names are still the same then it is going to work exactly like this.

So a good example that I've used on a system similar to this is where I had a front end and I started off connecting it to just a very simple data source just like this. And then the client loved it and they wanted to move on to something more complicated. So we changed the application to be something that was a big data system. It wasn't even the same language. It wasn't even a Rails app anymore. It was a Scala play application that did all kinds of cool Big Data kinds of processes. And what we were able to do was build it, keep the naming the same, and everything like that. And when it was time to migrate to the new system our front end needed a single line of code change and that was just the URL that we went and we grabbed the items from. I think that's pretty awesome and that's something that is very challenging to do.

Imagine if you did that with a monolithic kind of application where you have the entire system all in one app. If someone came to you and said oh I want you to change it so you change the programming language and everything like that you'd have to literally start over from scratch. But because of how this is structured we simply care about the data that's coming in. And if that fits what our data model looks like then we are good to go. Which makes a microservice kind of set up a much more flex interface.

Resources