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 - Services
- Part 10. Testing: Backend Testing — Unit Testing - Controllers
- 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
post about my system, I’m sure that there are better solutions for developing
this layer of my software :-).
The result is shown below
The first step is creating a new project using angular-cli:
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.
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.
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
will load our admin page (
Lastly, we must declare our components in the
AppComponent is the bootstrap of our app. This component only runs the
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 file is loaded by default when you're developing an Angular
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).
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
The CSS is quite simple and is shown in the following code:
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 of
Userwhich are not in the building.
usersPresent$: This is the observable which contains the list of
Userwhich 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:
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
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
- The operator
switchMapis used to cancel an unfinished request when a new request is made (to avoid several request being made at the same time).
- The operator
retryWhenis 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
mapoperator 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
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
isAbsent methods, which are shown subsequently:
These methods basically check if the user has been authorized by the system, and whether the action is
So, the complete controller code is the following:
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
subscribes using the
The template uses the if-else of the
ng-container to show a message when nobody has a key.
UserComponent code is the following:
In this case, we’ve defined four relevant attributes:
users$which contains the list of users' UID.
- The string
userIDwhich contains the userID selected from the template.
- The string
keywhich is the key that will be assigned to the user.
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
save method makes a request
POST to the backend with the object that the backend requires to save the information.
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.
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.