Now that you have gotten your feet wet with Angular observables in the previous exercise,
you're looking back and saying,
"Oh observables are not that bad after all,
they are fairly straightforward to make use of."
So, we get a bit more ambitious.
Let's go one step further and then make use of a built-in observable within Angular.
The params observable that comes from
the activated route service within our router module.
We'll now update the dish detail component,
to make use of the params observable from
the activator drought service to
support some new features within our Angular application.
Before we move on to update the dish detail component,
I want to introduce one more method in my dish service.ts file.
So open dish service.ts file and then we'll add a new method called
getDishIds and this basically will
return all the dish IDs is for all the dishes in our collection of dishes.
So, to do that, we'll use a return
and of method and before I do that,
this is returning an observable.
Of what kind?
This is returning an observable which consists of an array of
string and let's delay
it by 2,000 milliseconds or two seconds.
So, what is it that we want to return?
We want to take the dishes array,
extract out all the IDs from the dishes array and
then give it to our Dish Detail component,
because we need that within our Dish Detail component to implement
the new feature that we are going to support in our application.
Now, this is one way that I want to implement it.
Later on, we can see how we can do it in a much better way,
in one of the later exercises.
At the moment, with the way the service is implemented,
this is the best that I could do to implement this solution to demonstrate a new feature.
So, within the observable,
what I'm going to do is,
I'm going to take the dishes array and to the dishes array.
I'm going to use the map,
an array operator, the JavaScript array operator map.
So, the map operator,
how it works is that it takes each item from the dishes array and we can map
that item into another item and then construct
another array and then return that modified array.
So, for each item in my dishes array,
I'm going to take that item and then instead
extract the dish ID and then from the dish ID.
So, I will be ending up constructing a new array out of it,
which contains only the IDs,
so an array of IDs and we already see that the IDs are string,
that's what I'm going to return from my dish ID here.
That's it, the map array operator.
If you look up JavaScript array operators,
you will see what the map array operator does.
Indeed, the map array operator is also
interesting from the perspective of the observables,
because an observable as you saw,
can also be viewed as an array,
a stream of values,
which can be viewed as an array.
So, the map operator can also be used in observables.
But at this moment, I am using it simply as a JavaScript array operator.
Let's save the changes.
Now, we will go to the Dish detail component and then update the Dish detail component.
Now, within the Dish detail component,
along with the Dish,
I'm going to declare a variable called dishIds,
which is an array of string.
This dishIds will store all the IDs of
all the dishes that are in my collection of dishes in my menu.
Then, I will also have
two more variables called previous and next of the type string.
Now, why do I need these?
That'll become a bit more clear,
once you implement the view,
where I'm going to make use of this.
Now, what I will do here is that,
inside my ngInit earlier,
we did let id is equal to plus this route.snapshot params ID.
When I did this in the previous exercise,
I didn't explain to you what the snapshot was doing and why we're doing it this way?
Because you needed to understand observables,
before you understand what the snapshot means.
Now, in Angular, the activated route service provides a set of observables.
One of the observables is called a params.
What this params provides us,
is a way of obtaining the parameter values within my URL.
So, you saw that when you introduce the route parameters,
you introduced one of the route parameters as colon ID.
That colon ID becomes available as an observable.
So, whenever that value changes,
you can observe changes in that value and then take action correspondingly.
But, earlier when we implemented the dish detail component,
we didn't know about observables.
So, I couldn't explain what the snapshot was doing at that point.
Now, let me go back and explain exactly what the snapshot does.
When we use this as snapshot here,
what we are doing is,
we are taking one snapshot from
the route service and then we are obtaining
the parameter observable at that particular point of time.
The value of the params at that particular point of
time and then making use of it within our application.
Although that is not the ideal way of doing it.
Instead, so let's make use of the observable
directly and then respond whenever that observable changes.
So, how do we update this code to make use of the observable directly?
So, what we realize is that from the activated route service,
we have the params observable available to us.
So, we'll make use of the params observable and
anytime there is a change in where params observable,
we can initiate a change within our DishDetail component.
Let me illustrate to you how we will make use of
the params observable by updating this code.
So, going to this code,
what I'm going to do now,
is that I'm going to change this into saying,
"I have this route and from this route,
I have the params available already."
So, I'm going to remove the snapshot and I use the params.
So, with this params,
what I have now is the access to the params observable.
Now, since we have this params observable,
I'm going to use an operator on this params observable and then modified it.
So, the operator that I'm going to use is called switchMap operator.
The switchMap operator enables me to
make use of the params observable and you see the red squiggly line that means,
that I need to import the switchMap observable in here.
So, I will go in to
my inputs here and
then I will input rxjs and operator.
So, from the params observable,
I get params which is of the type Params.
So, recall that we imported the params from the router library here.
So, we implored to params here,
from the router library and also we imported the activation route service here,
and then we made them available within our constructor here,
through the activator route.
So, that's why we are able to do this route params to get hold of the params observable.
I'm going use the switchMap operator on the params
observable and then when I get the params observable,
what I'm going to do is to take that and then say, this dishService.
GetDish, and then this ID now should be obtained by using the params.
You see the parameter that we have there.
From the params, I'll say within brackets ID.
So, what happens is that whenever the params observable changes value,
which means that the route parameter changes value, then immediately,
the switch map operator will take the params value,
and then do a getDish from my dishService.
So, this would be automatically initiated,
and this will be available as the other
observable that is emitted by doing this switch map operator on this observable.
So, we are creating a new observable which is the getDish,
which is going to return the dish object here.
Now, once we get the getDish there,
then that can now be available as an observable.
I just subscribe to that observable using the subscribe here.
Then, there I obtain the dish.
This dish is obtained by doing this getDish here.
That dish, then I can make use to map it into
my dish variable that I've declared earlier.
So, this way, my dish now gets updated.
So, any time the params observable changes,
my dish will get updated to the new dish.
So, notice how I'm taking one observable, the params observable,
and then I'm mapping the params observable into another observable which is basically
going in fetching the dish value from my dishService,
and then making that available as an observable.
Then, I'm subscribing to that observable here, and then thereby,
I'm getting the dish value here,
and then I'm mapping the dish value or rather
making the dish variable equal to the dish value here.
Notice how by using the observables,
you are now able to take one observable then map it into another observable.
Why is this interesting?
If now, I have a way of modifying that route parameter,
then I will be able to switch between different dishes and see the different dishes.
Unfortunately, I don't have that in place.
So, I need to modify the dish components to enable me to do that.
Now, this is where I will include a couple of buttons into my view which when I click,
I will be able to navigate through my list of dishes.
Now, to be able to navigate through my list of dishes,
I need to know the set of all my dishes,
or at least I need to know the IDs of all my dishes.
That is the reason why I use this dishIds here, okay?
So, now you see why I included the dishIds method into my dishService.
So, then I start out.
I am going to first obtain
the dishIds by
calling the dishService,
getDishIds method at that point.
I am still working with the old way of doing things.
I have to realize that this getDishIds is sending out in observable.
So, I need to subscribe to that observable.
You see how you need to change your way of thinking when
you're operating with the variables here.
So, we would say dishIds subscribe,
and inside the subscribe,
we will obtain the parameter which is a string array,
and then I would say right there,
dishIds will be equal to dishIds.
See, how easy it is to
change your way of thinking when you want to deal with an observable.
So, the getDishIds is sending an observable,
and I'm subscribing to that observable and then I have the dishIds here available.
Then, also by subscribing to the parameter.
If I can change my parameter,
value route parameter value within my code,
then I will be able to navigate among the dishes.
So, let's implement that part.
This is getting interesting.
So, to enable me to do that,
so I will introduce another method here called setPrevNext,
and to this, I will send a parameter dishId.
Now, given my current dishId,
I wanted to be able to find the previous and the next dishId so that I can implement
my navigation between the dishes within my dishdetail component.
So, that is the reason why I declared the prev and the next.
Now, I need to be able to initialize these two values.
All that I know now is my current dishId.
So, using the current dishId,
I want to be able to find the previous and the next dishId.
So, in this method,
I'm going to do the previous and the next dishId.
So, here I will say,
let index equal to this dishIds.
Now, I know only the ID of my dish,
but the ID of my dish doesn't give me everything.
I need to be able to find the dish in this array of IDs,
so that's why I'm saying this dishIds, and indexOf.
You should know the indexOf operator on an JavaScript array.
The indexOf operator takes a value and then finds the index of that value in the array.
So, indexOf and the parameter here,
I will give is the dishId,
the dishId which I will obtain in a short while.
Now, once I obtain the index of this current value,
I can then map
the previous two dishIds.
So, this is where I
will do some little bit of tricky code here that will allow me to wrap around when I am.
So, notice that this dishIds array contains a certain number of values there.
If my current value of my dishId is the first item,
then the previous value will be the zeroth indexed item in my array.
But if my current value is the zeroth item,
the very first item in my array,
then I want to wrap around to get the last item in my array.
So, this is where I make use of the modulo operator with
some mathematical way of wrapping things around.
So, I would say this dishId
plus index minus one, and modulo this dishIds' length.
So, what I am doing is,
the previous value will be the current index,
index of the current value,
plus the length of the dishes minus one.
Now, if index is one,
index minus one will give me zero.
If index is zero,
index minus one will give me minus one,
but that's not what I want.
I want to wrap around and get the dishId length minus one.
So, that's why I am including this in there,
and then doing a modulo with the dishIds.
So, when index is zero,
I will wrap around to get the last item in my array.
So, that's how I initialize the previous value.
Then, the next value,
I will use the same approach,
but wrap around by saying index plus one.
So, if I am currently at the last item in the array,
then I want to wrap around to zero.
So, that's the other part of what I'm doing.
So, there, I am setting the previous and the next.
So, when should I set the previous and the next?
Anytime my dish changes,
I need to be able to initiate a call to this.
Now, where am I changing my dish value?
Looking back at the subscribe here,
notice that right there,
you are changing your dish every single time that our parameter changes.
So, at that point,
I want to reset my previous and next value.
So, I'm going to change this into a block of code here.
Then, I would say this setPrevNext,
and then the parameter would be dishId.
That is it. So, with this change,
what happens is every time my dish gets updated,
I will update the previous and the next also correspondingly from here.
So, with this little change,
my dishId will be the current dish.
The previous will be pointing to the previous dish,
and the next will be pointed to the next dishId.
So, I have all the three that I need.
Now, I can make use of these three values in order to implement the way to navigate
among the dishes in my template by including a couple of buttons into my template.
So, let's go to the dishdetail components template,
and then include a couple of buttons that enables me to navigate among the dishes.
Going to the Dish Detail Component while I am here,
let me take this ngIf from this card and then move it into the div up there.
I want to hide the entire div,
when my dish is currently not defect,
similarly for the comments also.
I find this to be a bit jarring
when my dish information is being fetched from the server.
So, I'll make this change for both the two divs that
one showing the comments and the other one showing the details of the dish in the card.
With these two updates,
inside my card actions,
I'm going to include
one more button of button type obviously and this button,
I will include the routerLink.
The moment you see router link,
you immediately say, "Oh,
I know what you're trying to do.
You're trying to convert this button to be able to navigate among
the different components within my service."
So, for the routerLink,
I'm going to provide an array of values and the array of values for the routerLink,
I'm going to do dishdetail.
Also, the second part.
See, this is the interesting part.
The second part of this array will be prev,
which is the variable that I declared in my component.
So, with this, this button will become a link to the previous dish in my list of dishes.
Spiffy, right?
So, with that, what else?
Let me close off the button here and then inside the button,
I'm going to use a Font Awesome icon
fa chevron-left.
So, that's it. This button will be a button with an icon there.
So, now we have one button here,
I'm also going to include another button here.
Before I include the button,
I will do span class flex-spacer,
you saw me using the flex-spacer in one of the earlier exercises.
So, you see what I'm doing,
I'm stretching to include the second button.
So, for the second button,
I'm just going to copy this
and then paste it there and then update this one to say prev.
Instead of previous, I will go next and then
fa-chevron-left to fa-chevron-right, and that's it.
Let's save the changes to our Dish Detail Components,
and also the remaining files.
After this update, let's go and check out what our application does.
Going to my application in the browser,
let's go to the Menu component.
We see the Menu and let's select one item from the Menu here.
So, notice that now this item from the menu dish is displayed here.
Notice at the bottom,
we have two buttons here, left and right.
When we click on the left button,
note how we are able to move to the previous dish in the list of dishes.
I can keep doing left again and keep navigating to the left and the right.
Notice that when I do this,
my actual view is not getting reloaded,
instead I'm only going in fetching
the dish-related data from the service and then updating my view here.
The reason we are able to do this is because we
have the observable, the params observable.
So, anytime I click on these two,
left and right, the params observable,
notice how at this moment the address shows dishdetail zero.
If I click on the right,
then that will be updated from dishdetail zero to dishdetail one.
That parameter change, and that will cause my observable to
go and fetch the new dish from the dish service.
Then, the new dish is fetched,
the dish variable in my component gets updated,
and when that gets updated,
my view also gets updated.
So, notice that I'm using the same view,
I'm able to navigate among the various dishes that are part of my list of dishes.
This is the interesting part that you can get by using the params
observable from the activator.service within your Angular application.
Interesting way of making use of observables bills our Angular application.
Of course, the way I have implemented it,
I did it a little bit of a contrived way of doing things.
I had to get hold of the list of dish IDs.
I had to modify the previous and the next and so on.
If you had a way of obtaining the previous
and the next value automatically from your service,
whenever you fetch a dish,
then the implementation of the score will become a bit more easier.
Right now we don't have that.
So, I worked around the problem because I just wanted to illustrate the way
of using an observable like this within your Angular application.
If we can implement the service that will return the
previous and the next value whenever we fetch a dish,
then all this headache will be a lot more simply solved.
Let's postpone that idea to a later stage.
But at the moment, you see how we are making
use of the params observable in order to implement
an interesting way of supporting a new feature within our Angular application.
This completes this exercise.
This is a good time for you to do a git commit with the message RXJS Part two.