Lesson 22: More Observable fun!

When we are pulling data from our service, we are simply grabbing an array. Nothing fancy about it, just calling one variable and bringing it in.

But many of your calls to a service won't be like that. Quite often, especially when you start to deal with HTTP requests, the result will be an Observable. So let's create one.

Transforming our array into an Observable

The process of transforming our array of rooms in our room service into an observable is actually very easy. First, make sure you import the RxJS operator we will be using: of.

import { of } from 'rxjs/observable/of';

Next, let's change the type of our array from an array of rooms to an Observable array of rooms.

Our linter gets mad now because it's expecting our array to be an observable, but it's not. Let's fix that.

Finally, wrap our array in the rxjs of operator like so.

That's it. Yes, by just wrapping our data in an of, we've transformed it from a static value into an observable.

That's it? Is the next part gonna be this easy?

Sorry to disappoint you, but no. Because we changed our room data in our service into an observable, that means anywhere we are injecting that data into needs to now handle that data properly like an observable. Where are we pulling in room data from our room service? Our nav bar.

Handling our data correctly

Go to the navigation component. You can see where we grabbed our room data as a plain array, used Array.map() to change it from a type Room to a type NavigationItem and then adding it on to our static array.

We still want to do that, but because it's an observable, how we do it is different.

This is admittedly not the easiest topic to wrap your head around, so take your time with it. Let's go step by step.

Begin the Pipe

Let's call the observable and begin our pipe:

this._roomService.rooms.pipe()

So far so good. Lets begin our rxjs map and get ready to transform our rooms into navItems.

this._roomService.rooms.pipe(

map()

)

So inside our rxjs map will go our transformation, where we utilized Array.map:

NOTE: Because we are using shorthand on the array function for our rxjs/map, the resulting line will be implicitly returned. You may have used the standard () => {} notation and returned the result of our Array.map which is totally fine.

this._roomService.rooms.pipe(

map( rooms => rooms.map(eachRoom => {

})

)

)

Our transformation technique will be the same thing:

Use Array.map to transform our Rooms into NavigationItems

this._roomService.rooms.pipe(

map(rooms => rooms.map(eachRoom => {

return {

title: eachRoom.title

title: 'room/' + eachRoom.id

}

})

)

)

That completes our transformation. It's a little weird to look at because we are using Array.map inside rxjs/map. With all these maps, you'd think I was building an atlas. Bad map jokes aside, we've handled our transformation. Lets throw some types on our variables inside there to help us understand what is going on:

this._roomService.rooms.pipe(

map( (rooms:Room[]) => rooms.map( (eachRoom:Room) => {

return {

title: eachRoom.title

title: 'room/' + eachRoom.id

}

})

)

)

So we take our initial value going into the pipe as an array of rooms, then array.map it passing the value of each room. We should have a value type Navigation Item if we subscribe after the pipe. Lets check it out:

this._roomService.rooms.pipe(

map( (rooms:Room[]) => rooms.map( (eachRoom:Room) => {

return {

title: eachRoom.title

title: 'room/' + eachRoom.id

}

})

)

).subscribe( (navItems:NavigationItem[]) => {

});

So if you're linter isn't bugging out on the navItems being of a type navigation item, you're good! We did the transformation and typed our result just to make sure the result is exactly what we wanted. Now that we have our two array, we can use the same for..of technique to combine them.

this._roomService.rooms.pipe(

map( (rooms:Room[]) => rooms.map( (eachRoom:Room) => {

return {

title: eachRoom.title

title: 'room/' + eachRoom.id

}

})

)

).subscribe( (navItems:NavigationItem[]) => {

for (const eachItem of navItems) {

this.navArr.push(eachItem);

});

Check out your navbar now, we should have the same navbar, this time with observables.

O-M-G. My brain hurts.

Yeah, this rxjs business can be tricky to learn but it is really important. When you start dealing with data that you get from a database (and you will), then you will need to know how to deal with observables, how to manage the data it is emitting, and how to display it.

Lastly, one thing that is worth mentioning with observables, you can do all the .pipe you want, if you do not subscribe to an observable, the code will not execute. There is one example of an exception to this, but that will be in our next lesson.

We're Not Done

WHAT

Don't worry, this won't be nearly as bad. I promise

When you Subscribe, you must Unsubscribe

One rule you should remember, if you create a subscription, you need to unsubscribe somewhere. You can either use rxjs/take, or throw an unsubscribe inside a method like onDestroy. Since we're using a bunch of rxjs already, I'll go with take.

this._roomService.rooms.pipe(

map( (rooms:Room[]) => rooms.map( (eachRoom:Room) => {

return {

title: eachRoom.title

title: 'room/' + eachRoom.id

}

})

), <-- don't forget the comma

take(1); <--- I imported take up top. You should too.

).subscribe( (navItems:NavigationItem[]) => {

for (const eachItem of navItems) {

this.navArr.push(eachItem);

});

Now we're done! Whew! One nice thing about all that business we just did is that when we implement firebase, since we're already handling an observable of rooms from our service, it'll be a breeze.

Here is a screenshot of my navigation.component.ts, just to make sure we're square.

nav component ss