This post is part of a Series of post which I’m describing a clock-in/out system if you want to read more you can read the following posts:
- Part 1. Clock-in/out System: Diagram.
- Part 2. Clock-in/out System: Basic backend - AuthModule.
- Part 3. Clock-in/out System: Basic backend - UsersModule.
- Part 4. Clock-in/out System: Basic backend- AppModule.
- Part 5. Clock-in/out System: Seed Database and migration data
- Part 6. Clock-in/out System: Basic frontend.
- Part 7. Clock-in/out System: Deploy backend (nestJS) using docker/docker-compose
- Part 8. Clock-in/out System: Deploy frontend (Angular 2+) using environments
- Part 9. Testing: Backend Testing - Unit Testing
- Part 10. Testing: Backend Testing - Integration Testing
- Part 11. Testing: Backend Testing - E2E Testing
- Part 12. Testing: Frontend Testing - Unit Testing
- Part 13. Testing: Frontend Testing - Integration Testing
This is the first post about the frontend for our clock-in/out system, which already has a basic, functional backend working. The frontend will be developed using the JS framework Angular due to it being the best framework, in the sense of software architecture, (my intent isn’t to unleash a war about which is the best/worst JavaScript framework). This is a post about my system, I’m sure that there are better solutions for developing this layer of my software :-).
The result is shown below
Well…. Let’s go!
The first step is creating a new project using angular-cli:
In our context, a routing is not required, because our frontend is not an SPA (OMG! So why are you using Angular then? Because this is a simple tutorial to show how to integrate NestJS + Angular).
The next step is to install several libraries that are dependencies in our code (Angular Material):
Our project has three important points:
- AppModule: This is the main module, which is responsible for launching the other modules.
- UserComponent: This component is used to add new users and their keys (it’s only for admin purposes, although there isn’t any security).
- TicketingComponent: This is the most important component, since this is the one which refreshes the screen with the information about the users who should be working in the building.
Now, I’m going to show and explain each of the modules.
AppModule
This is the module which is used to launch the other modules. In the following code I’m loading the modules:
- Angular Material:
- MatTableModule: The table that will show the list of users which are in the building.
- MatInputModule: Form’s input that will be used to add the users-keys pair.
- RouterModule: This module will be used to load the clock-in/out and admin pages.
- FormsModule: This module is required to use template-driven forms in our project.
- BrowserModule and BrowserAnimationsModule: These are the modules required to use Angular in the browser (and the animations).
- HttpClientModule: This module will be used to communicate the frontend and the backend using the HTTP protocol.
The Route[]
are the routes to load our components. In our case it's very easy,
because the default path will load TicketingComponent
and the path /user
will load our admin page (UserComponent
).
Lastly, we must declare our components in the AppModule
: AppComponent
,
UserComponent
and TicketingComponent
The AppComponent
is the bootstrap of our app. This component only runs the
router-outlet
.
Constants and environment
In any software we develop, we need different constants and environment variables, i.e, http://localhost:4200 is the traditional URI to develop an Angular app, although you need to change the domain or the port when you deploy your app. For this purpose, Angular provides us with configuration to change between different environments.
So, the file AppSettings
can define every our constants. The most important
constant is the APIENDPOINT which is provide from the file environment
.
The environment
file is loaded by default when you're developing an Angular
app:
The only difference in the environment.production.ts
file is the APIENDPOINT_BACKEND
constant, which contains the name of the machine on which our app is deployed (In our case, a docker container).
Ticketing Component
The ticketing component is the most interesting piece of code in this project, due to it having been developing using RxJSto make the system in near-realtime. This example doesn’t use redux, so the double data-binding is used to refresh the template, from the logic part. This component’s template is the following.
You may note that the template has several Observable$
, which are rendered using the pipe async
. For example, in the following code, the span
tag redered the result of the subscription of the observable timestamp$
. This pipe is a syntactic sugar for the traditional subscribe
method. You can read more about this pipe in the official documentation.
Other interesting point of the template is the use of the component Material Datatable
which can received a set of data to be rendering in a table or an observable using the input [source]
but in our case the Datatable
will receive a set of data (after that the pipe async
will do its job). Futhermore, the data are show in two different tables, so the data are separated in two sets using the method middle
.
The CSS is quite simple and is shown in the following code:
Although this post isn’t about CSS, you must know NEVER to use id
's in styling your web (you can read more about this fact in dev.to, CSSWizard
and developingdesigns).
Our CSS file is simple, since it’s only styling our table (which must have a width
of 49.50% and different typography size adjustments).
I will now reveal the most interesting piece of code in this post, the TicketingComponent
, which has the subsequent attributes:
The description of each of our attributes is:
usersAbsent$
: This is the observable which contains the list ofUser
which are not in the building.usersPresent$
: This is the observable which contains the list ofUser
which are in the building.timestamp$
: This is the observable which contains the timestamp from the server.displayedColumns
: The array of columns which will be shown in the table.
It is very important to remember that we’re using observables in our code to provide us with the power of stream’s manipulation by using the RxJS operators. These observables are subscribed using the pipe async
in the template.
Our next step is the component constructor, where the real magic appears! You must understand the streams in RxJS to be able to understand the following code:
This code does the following:
The observable interval$
is created using the timer
operator, which in turn
will trigger a call each 3000 ms. In the subsequent line of the code you can see
how the observable data$
is created from the observable interval$
which runs
a http request using the httpClient
service.
The get
request then return an object comprising a list of users and a timestamp (from the server). Two sections of this code fragment are particularly
relevant:
- The operator
switchMap
is used to cancel an unfinished request when a new request is made (to avoid several request being made at the same time). - The operator
retryWhen
is used to handle the server errors. For example, if the connection is lost in the client or server you will need to retry again the request. So, when the code has an error, the request will be retried in 3000 ms.
Ok, now the observable data$
has a stream containing information about the list of users and timestamp. The observable users$
is created from the observable data$
which does a destructuration in each stream of data (this is the reason for the map
operator being there). If you have understood the previous code, you can imagine how the observable timestamp$
is created. This timestamp is in unix format, we need to transform it to the DATE_FORMAT (DD/MM/YYYY).
Perhaps you can now imagine how the usersPresent$
and usersAbsent$
observables are created from the users$
observable. For these observables you must use the RxJS map
operator to create a new observable, using the Array.prototype filter
method. The last step is creating the private isPresent
and isAbsent
methods, which are shown subsequently:
These methods basically check if the user has been authorized by the system, and whether the action is INPUT
or OUTPUT
.
So, the complete controller code is the following:
User Component
The last component of our basic frontend is the UserComponent
, which is a simple form to add users and keys to our database. The idea to build this component is the same as the one used in the TicketingComponent
. Therefore, the template to do the operation subscribe
s using the async
pipe.
The template uses the if-else of the ng-container
to show a message when nobody has a key.
The UserComponent
code is the following:
<span class="figcaption_hack">Type caption for image (optional)</span>
In this case, we’ve defined four relevant attributes:
- Observable
users$
which contains the list of users' UID. - The string
userID
which contains the userID selected from the template. - The string
key
which is the key that will be assigned to the user. - Observable/Subject
update$
which allows us to know that the action updated was done successful.
The constructor is very similar to the constructor in the TicketingComponent
, due to it recovering the list of users' UID from the backend, by using the switchMap
and map
operators.
Finally the save
method makes a request POST
to the backend with the object that the backend requires to save the information.
Conclusion
In this post I’ve explained my basic frontend, developed with Angular and RxJS to ensure a near real-time system (using polling as the technique to connect with the server).
The GitHub project is https://github.com/Caballerog/clock-in-out.<br> The GitHub branch of this post is https://github.com/Caballerog/clock-in-out/tree/part6-basic-frontend.
Originally published at www.carloscaballero.io on January 18, 2019.