Now that we have completed updating the server-side,
it's time to move on to the Angular Client.
For your convenience, I have provided you with
the GitHub repository that contains the final version of
the Angular Client that is able to communicate with
our REST API Server and then render the various views for our client.
So, I have taken the Angular application that we
developed in the second course of this specialization,
and then modified it.
We will take a brief tour through the code in a little bit later.
Now, you will clone the Git repository to your computer,
and then start-up your Angular Client,
and then see it communicating with our server-side.
Let's see the details next.
To get started with this exercise,
at your terminal or command window,
go to your convenient location and then at
the prompt type git clone
https://github.com/jmuppala/conFusion-Angular6.git.
Then, let the Angular application be cloned to your computer.
Once the cloning is complete,
move to the conFusion-Angular6 folder
that has just been created when you clone this git repository.
Then you will immediately notice that there are a bunch of files already there.
Now, at the prompt, type npm install,
in order to install all the node modules on
which this Angular application is dependent upon.
So, once the node modules or all installed,
then we can start up our NG serve to compile and serve up our Angular application.
Once everything is installed by npm install,
then make sure that your MongoDB Server is up
and running and also your REST API Server is also up and running.
Otherwise, your Angular Client will not work.
So, now that you have installed your Angular Client on your computer,
at the prompt type ng serve and
your Angular application will be compiled and served up by ng serve in a short while.
Once your Angular application is successfully compiled,
then go to a browser and open your Angular application in the browser.
Going to the browser,
at the address bar,
let me type in localhost:4200 and
you will see that your Angular application gets
started up and is visible on the screen here.
You will notice immediately that your Angular application
is able to fetch the data from the server and then render
the various parts of your Angular application just like what you
did at the end of your Angular course.
When you go to talk about folder,
you will also see that the About page also renders the information as such.
In the menu, you see the items in the menu being
displayed just like we saw with the Angular application.
Now, in addition, I have added in
one more page to the single page application called My Favorites,
which you cannot navigate to because no user is logged into the system.
So, you see that I don't have any user logged into the system.
So, that's why there's no point in indicating my favorites,
because you don't know which user,
as favorites, should be shown here.
The contact page as usual.
Now, let's login.
To login to our application,
we'll click on the Login button and then you would see that the Login dialog pops up.
Then, let me login as one of the registered users,
which we registered as part of this course earlier.
So, once you login,
you will immediately notice that,
to the right corner,
you see the Login button has now turned into
Logout and the User's name is indicated here.
So, whoever is logged in is indicated here.
You could also, in principle,
replace this with the image of the user.
Now, for that you need to do more modifications to
both the client and the server in order to support the image display here.
But, at the moment, I'm just showing you how we
can easily display the user information here.
So, this require minor modification to the head of
component where I will be able to retrieve
the username from a service
called as the OAuth service which we will talk about in a short while.
So, once the user logs in,
then if you now click on My Favorites,
you would notice that there is nothing in my favorites for the user.
Obviously, because the user has not yet chosen any favorites.
So, let's now go to the Menu and then in the Menu,
let me select one item and then they will add this dish to our favorites.
So, going down below here,
you see the heart symbol there,
click on that and you'll see that this would be added into The Favorites.
The fact that this has been added into The Favorites is indicated by
the change in the icon here from a unfilled heart to a filled heart.
Now, if you go to any other dish,
you would notice that this has an unfilled heart,
which means that this is not yet among our favorites.
So, let me add in one more item to
my favorites and then let's add a third item to my favorites also.
So, now, you will see that if I now go to My Favorites,
you will see a set of My Favorites being displayed here.
So, you saw that I just added these three dishes to My Favorites.
So, these are being displayed here.
So, this is tracked by using the favorites end point on the server-side,
which I've implemented as part of your last assignment.
So, if you have done that assignment correctly then this part should work as expected.
Now, we can also modify My Favorite.
So, for example, at the right corner here,
you see this Delete toggle here.
So, let me turn that on.
So, when you turn it on,
you immediately see the three red crosses
appear at the bottom of these three items in My Favorites.
When I click it off, then they disappear.
So, this is one way of turning on the delete buttons on your My Favorites and then.
So, let me go ahead and delete one of the items from My Favorites.
So, when I click on that button,
you'll notice immediately that that item is deleted from My Favorites,
and immediately disappears and My Favorites gets
updated and you'll see the resulting value being shown here.
At the same time,
this Delete toggle is turned off,
so that the delete buttons are no longer visible.
I can always again turn on the delete buttons by clicking on the on and off here.
So, notice that the My Favorites will be displayed only
for users that are logged in to the system.
So, you immediately notice a set of changes that have been
done to the Angular Client in order to
support some of these additional features that you see here.
You also saw the Login and Logout feature being supported here.
So, when I click on the Logout button,
you immediately notice that the user gets logged out
and the username disappears from there,
and so the button also is turned into the Login button.
So, with this, you notice how my Angular client has been
updated to make use of the new REST API server
in order to support the favorites being saved and on the server side and
then automatically being reflected into my client application as shown here.
Let's take a brief tour through
the Angular code that I have supplied for you in the GitHub repository,
and also see how we have modified parts of the code in
order to implement the updated Angular application.
You will notice that there is a new service
that I have introduced here called auth service.
The auth service takes care of
all the authentication-related actions for the Angular application.
So, within the auth service,
you would immediately notice that I have
a set of information that I have configured here.
Since you already know Angular,
you should be able to easily process whatever is written here.
Note in particular that within the auth service itself,
I am storing information like,
for example, the tokenKey,
which is the key for the local storage where I am storing the JSON Web Token,
and also some additional variables
here where I am tracking the user name and where I am authenticated,
and also keep track of the the JSON Web Token here.
Now, let's begin by looking at when the user logs in.
So, when the user logs in,
then this function called the login will be called in our auth service here,
and when the login is called in the auth service,
this will be passed in a parameter called user which contains
the username and password as part of the user JavaScript object.
So, at this point, I am doing an HTTP post to the baseURL plus users/login.
Note also that here,
I am supplying the AuthResponse as the qualifier here.
The AuthResponse is nothing but an interface that I have implemented here,
at the top here.
So, this interface essentially specifies
the information that is going to come back as
my authentication response from the server side,
the structure of the JSON data that is coming
back from the server side and the corresponding JavaScript objects.
So, you notice that when we updated the server,
we made sure that the server is sending back the status,
the success, flag, and the token in the string index.
So, that information is obtained in here.
Now, when I am doing a post on that,
I am passing in the username and password to the post on this /users/login endpoint.
When the reply comes back from the response,
the response message itself will contain, as we saw,
the success, the token field,
and also the status message here.
So, from the response message,
I'm extracting the token and then passing it in to this function which I implement here,
local function here, called storeUserCredentials.
So, that will be returned to my application and will be stored in my client side,
into the local storage.
Then, from this function,
I am returning this information back to
the calling function from where I initiated the login process.
So, this way, I will be indicating to my login component that the login was successful,
and then I will also pass in the username to my login component,
which then will pass that information onto the header component,
and the header component uses that to reflect the username onto
the toolbar at the top there,
and also we catch the error here.
So, this is a very simple implementation of how we performed the login.
When you do the logout,
notice what I do when the user calls a logout.
When the user calls a logout,
I just simply destroy the user's credentials,
which includes throwing away
the JSON Web Token that I have obtained when I logged into my application.
Then, some additional helper functions that I have implemented here called
getUsername is logged in and getToken,
which will be useful from my other applications,
other components and services.
So, now let's look at some of the helper functions that we have implemented here.
So, the loadUserCredentials will retrieve the credentials from the local storage.
So, you notice that here I am calling localStorage and then saying getItem.
So, the localStorage here is using
the web browser's local storage that is supported by standard web browsers,
and storing the information there,
and then it is retrieving this information there.
Then, from there, the credential information,
if it is valid,
then those credentials will be set up here.
So, all these variables that I have defined here will be initialized with
the appropriate information after being retrieved from the localStorage.
Now, similarly, the storeUserCredentials function
that I've implemented here is called from the login method.
When this information is passed in,
the credential information is passed in,
that information is stored in the localStorage at this point with that key.
Then, the useCredentials basically
sets up all the variables that I have in the auth service.
So, it sets up the token,
it sets up the username,
and then it sets up the isAuthenticated function here.
So, notice that the username itself here,
I have declared it as a subject,
which is nothing but an observable here.
So, that is why whenever I say,
sendUsername here, I say credentials username.
So, this sendUsername implemented right at the top here,
notice that this is where the observable value is sent back.
So, that's why I'm saying this username next.
So, the observable.
Will send back the name that is supplied as
a parameter to whoever has registered themselves to observe this observable.
So this observable, I'm going to be observing this from
my header component and so that way whenever the username changes,
either from undefined to a given username or vice versa,
then I can update my toolbar in
the header component to reflect whether the user is logged in or logged out.
So that is set up by using this useCredentials.
Now the destroyUserCredentials basically sets the authentication token to undefined.
It clears the username and then sets the IsAuthenticated to false and then
removes this information from my local store it so
it's basically throwing away the jsonwebtoken at this part.
So that is how you love out a user.
So once the jsonwebtoken is lost the user can no longer
authenticate himself or herself on
the server side because the token is no longer available to us.
So that's how we implement the logout function within our application.
So take some time to go through the
auth.service here to see how I have implemented the various functionality.
Now, by this time,
I am sure you are very familiar with how Angular applications are implemented,
so it should not be that difficult for you to understand how this code is implemented.
Now one more function that I would draw your attention to is the check JWT token, here.
This function can be called at any point to check and make
sure that our JSON web token is still valid.
So this is where I am sending the get request to users slash check JWT token.
Recall that we implemented this route on the server side in the user's.js and.
So from there we will be able to verify whether the jsonwebtoken is still valid or not.
If the jsonwebtoken is not valid,
then we're going to destroy
the user's credentials and then expect the user to log in again.
If the jsonwebtoken is valid then it's okay and
we can proceed forward with allowing the user to
to recognize that we are logged in.So even if you close
your browser and then reopened the browser and then reopened your Angular application,
if you had logged in earlier and the jsonwebtoken was saved into the local storage,
it can be retrieved from there and then
your login status will be restored within your Angular application.
If the json web token is expired,
then we will not be allowed to log in.
So every time you refresh your Angular application in your browser or
reload your Angular application in your browser you're going to be
checking the jsonwebtoken to make sure that it is still valid.
If it is not valid then the user will be
cleared out and so you will have to log in again.
If not, then the logged in user's information is retrieved
from the localStorage and then initialized within my Angular application.
Periodically, if at any point in time your server
responds with a 401 Unauthorized message,
even at that point,
we will again crosscheck to see that the jsonwebtoken is valid and then allow that.
We will do that using something called the Http interceptors.
Let me take a tour to that part of the code,
and then explain to you how the interceptors work in a short while.
So now in addition to authService,
in the services folder itself you will see this file called authInterceptors.ts file.
So open this and you will see that here I have implemented Http interceptors.
Now what this Http interceptors do,
this is supported with the Http client which comes as part of Angular 4.4.
What the http interceptors do is they can intercept
outgoing requests messages make
modifications to the request message before it is sent out.
Similarly, they can intercept incoming response messages and then modify
the incoming response message before
the response is handed over to the Angular application.
So through interception, so how does this interceptor work?
So to make this interceptor work,
we implement, as you see,
a class called as an HttpInterceptor by extending the HttpInterceptor.
So here I have implemented the AuthInterceptor.
So what does this AuthInterceptor do?
This AuthInterceptor is basically capturing outgoing requests.
So to capture an outgoing request you call this function called intercept
and then this will give you access to the request and the next.
So you could you could chain in
a bunch of interceptors one behind the other if you so choose to,
so that they can process the outgoing requests one after another if you so choose to.
What this intercept does is it gives you access to the Http request message.
So when I get access to the Http request message,
you would notice that here I am injecting the auth service.
Now unlike the way we were injecting services into components,
here I'm showing a the use of an injector.
An injector is part of the Angular code.
So using an injector you can inject services or
other components into other services or components.
So here you see that I am injecting the auth service in here by using this.
Now one other reason is that if I directly inject the service into my constructor,
it will develop a circular dependency between the interceptor and the authService,
and that will cause your code not to work.
So this is a work around to the problems.
So that your interceptor- because I need the
authService,in order to get hold of the token from
the authService and authService in turn depends on this interceptor so that's
why in order to break the loop I am explicitly injecting the authService in this case.
Now this is something that I have figured out by trial
and error I initially went ahead and simply injected
the odd services to the constructor and then found that
Angular was not compiling the code then I figured out that
there was an error there and then after doing some Google search then
found out that this is an alternative way of handling
the same thing and this works out much better for our application.
So once I inject the authService from the service I get
hold of the token and then notice what I am doing here.
Here I'm saying clone const auth req req clone.
So we will clone the request and then we will set up in the headers.
So we'll say req headers set
authorization and then notice what I am
setting up the authorization header to be, bearer.
Plus authToken.
So in the authorization header I am setting up the bearer and authToken here.
So that is how I am setting up
the authorization header in
the outgoing request message in my Angular application. So right there.
And that's how I ensured that all outgoing requests will
have the authorization header set up before they are passed to the server side.
And then after that, we will simply pass it on to the next interceptor, if it exists,
or onto the outgoing queue,
so that request message will be sent out to the server side.
Similarly, I have another interceptor that I have implemented here.
This interceptor intercepts any unauthorized reply messages
that are coming back from the server side.
So, if the server replies with a 401 Unauthorized reply message.
So at that point again, same thing,
I have set up the authService here,
and then, inside here then I will say,
do handle if error.
So this is where I will get hold of the HttpEvent here.
The HttpEvent is a lower-level object than a request,
but that allows us to get access to the incoming reply message,
and then so we will check to see if there is an error,
and then if error is an instance of HTTP error response,
and if the error status is 401.
So which means that I have just detected that the server sent back a 401 error message.
So which means that there were some authorization problem on the server side.
Then at that point I will check
the json web token to make sure that the json web token is still valid.
If it is not valid,
then I will discard my credentials and expect the user to login all over again.
So that way I will ensure that if my json web token
expires in the process of trying to fetch data,
that will still be intercepted by here because if the server responds with a 401,
I will intercept that and then clear out
my json web token and I expect the user to login all over again.
We can also redirect the user to the login page if you want to,
but with the Angular application it's a bit more complicated
to do that and I didn't want to confuse you by doing all that.
Instead, I am simply logging out
the user at this point and then destroying the user credentials,
so that the user will be expected to login at that point.
So, simple handling of how we inject the authorization header into the outgoing request,
and also intercept any 401 unauthorized messages
that are coming back from the server side.
So, you see how these additional changes need to be
made to your Angular application in order to make it work with your server side.
With the authentication part set in,
if you didn't have authentication and if you were just going to
access the rest API endpoints and do get operations,
you don't even need to worry about any of these things.
No authentication is required.
Data can be fetched very easily.
But for any post,
put and delete operations,
we need to support all these features.
So that's the reason why I have implemented the authentication part into my application.
Also as I illustrated earlier,
when no user is logged in,
then we will not be able to navigate to the my favorites endpoint,
but when a user is logged in,
then we will be able to see the my favorites for the specific user.
This is implemented by using route guards in Angular.
Now to implement these route guards,
I have implemented another service here called the AuthGuardService.
So in this AuthGuardService,
we implemented this method called canActivate method which
we will subclass in order to implement this.
And in the canActivate method,
the implementation will return a boolean,
and in this case,
what we check for is if this user is logged in,
then we will allow the user to navigate.
Otherwise, you will navigate the user to the home endpoint.
So that's the way the route guard is implemented in this case.
Now, to make use of this route guard,
we will go to see how the routes are updated to make use of the route guards.
So in the routes.ts file,
you can see that for the favorites component,
we see that we specify the path as favorites and the component as
FavoritesComponent and then we specify this other field
called canActivate for which we specified AuthGuard as the parameter here,
and this AuthGuard is nothing but the AuthGuard service that we have implemented,
which is imported here.
So this way when the AuthGuard evaluates to false,
then you will not be allowed to navigate to the favorites component,
instead, you will be redirected to home,
but when the user is logged in then obviously the AuthGuard will evaluate to true,
and in this case,
then you will be able to navigate to the favorites route there.
Now also similarly there are minor modifications to the remaining components in there,
which allow you to to deal with
the intricacies of how your client and server will talk to each other.
In particular, note how the header component has been updated to
reflect the user's logged in status in the toolbar at the top.
That is again another interesting change that I have
made to the header component in our application.
So I will leave that as an exercise for you to figure out how that has been implemented.
Very simple code there.
Should be easy for you to figure that out.
So with all these changes, now,
my angular client is able to interact with my server.
Let me again show you how we can post some comments to the server,
and then see the comment immediately reflected into the dish.
So, again going back to our application,
we can now go to the menu and then pull up any dish here,
and I can post comments to the dish here,
so I would immediately set up the rating here,
and my comment value here.
Notice that I am not inputting
my username or my author's name here in the form here.
Now of course, in order to submit a comment,
you need to be logged in.
So if you are not logged in this comment will not be accepted by my server.
So let me first log myself in.
So I can login here.
And the moment I login,
you immediately notice that header toolbar is updated to indicate my status.
Now I can post this comment.
And you would notice that when I post the comment,
the comment is added into the list of the comments,
and also notice that the author field is automatically filled in here.
Because that is how we set up our server side.
In the comments field,
we have set up our user as
a reference to the user information
that we store in our server side,and since we use the populate,
Mongoose Populate on the server side,
the user information is automatically populated
into the incoming comments from the server side.
So, that is how you notice how I am leveraging what
the server already provides for me to automatically fill in the details.
So, minor changes again even into
the dish detail page to reflect the use of the comments support on the server side.
With this I complete the quick illustration
of the Angular client that we have implemented as part of this exercise.
And I hope that you go through the details of the code in the Angular client also,
and then reflect back on what you have learned in the Angular course and see
how the modifications enable us to implement
in a modified angular client which is now
able to communicate with the server and then support all the features
that we originally intended to implement as part of both the client and the server side.