This article outlines the practices we use in our application. We’ll also go through some general coding guidelines to help make the application cleaner.
1. Modular approach in Angular App
Angular provides us to create our app in modular fashion. As your application grows, it becomes cumbersome to manage the different parts of an application. Modules in angular are a great way to share and reuse code across your application.
Let’s start with default module of angular app i.e AppModule
.
a) App Module: Angular Default Module
App module
is the root module of any angular app. Every application has at least one Angular module, the root module that you bootstrap to launch the application.
b) Shared Module
Shared module
can have components, directives and pipes that will be shared across multiple modules and components, but not the entire app necessarily.
c) Feature Module
A feature module
is an angular ordinary module for specific feature. The main aim for feature modules is delimiting the functionality that focuses on particular internal business inside a dedicated module, in order to achieve modularity. In addition, it restricts the responsibilities of the root module and assists to keep it thin. Another advantage - it enables to define multiple directives with an identical selector, which means avoiding from directive conflicts.
2. Use of lazy loading a feature module
Angular best feature of feature module is its lazy loading
. Lazy loading of any feature module is achieved by angular routing. In other words, a feature module won’t be loaded initially, but when you decide to initiate it. Therefore, making an initial load of the Angular app faster too! It’s a nifty feature.
Here is an example on how to initiate a lazy loaded feature module via app-routing.module.ts
file.
|
|
3. Shortening the import path
Sometimes, we import paths are very long like this:
This is not the ideal situation to use this nested path. You can shorten this import path by doing this setting in tsconfig.json
file. Set the @app
and @env
variables in this file.
|
|
Once you have set up these path, you can now use short import path like this:
import { environment } from ‘@env/environment’;
You can check this link as well for more information.
Shorten TypeScript Imports in an Angular Project
4. Typings
One of the cool feature of Typescript is combining the multiple data types to make it easier to work. Look at the below example for better understanding.
|
|
In the above code, we can see, our createdDate
field is combination of both string
and Date
type. Normally, we have to deal date format either in string format or date format as per our requirement. So, this will handle this very efficiently.
Also, you can restrict the value of any field in advance, so that a field can have only these values, like this:
|
|
Now, in the above code, a status field can have any of these three values. TS complier will throw an error if any other value is assigned to it. In another way, you can achieve this by using enumeration
as well.
5. Component Inheritance
One of the less discussed feature of Angular is component inheritance.
Most of the time, for sharing functionality or data we use services/providers across the different components. What if, instead of common data functionality, you want common UI functionality? For example, consider a simple scenario where you want to navigate from one component to another using buttons. A simple way to implement this is to create a button, call a method in the code which then uses the Angular router to navigate to the page. And what if you didn’t want to repeat the same code in each component? Typescript and Angular gives you a pretty easy way to handle this encapsulation; welcome to the world of inherited components.
Photo: Medium
I have found a very informative post on component inheritance. Click the below link for more information.
PART 1 — The Case For Component Inheritance In Angular
6. Use Namespace to import multiple interfaces
You can organize all of your interfaces into the TypeScript namespace and then import the single namespace in your working class.
This is how we are using our interfaces without namespace.
|
|
|
|
By using namespace, we can eliminate the needs to import interfaces files.
|
|
|
|
7. Delegate complex expression from template to component class file
In order to make our template simple and understandable, we try not to write any complex logic expression in our component template file. If we are checking just expression like simple compare or evaluate true/false value, then it is fine to write in our template file. If our expression is longer and complex, then it is better to move this from template to component class file.
Let’s take an example of this. For example, you want to add a ‘has-error’ class to all form controls which are not properly filled in (not all validations have been successful). You can do this:
|
|
If you notice line 6, we have written a very long and complex expression for 'has-error'
class in our template file, which looks ugly as well as hard to debug. We can make it simple by removing it from here. All this logic will be moved to the component class file.
|
|
Now we have just a nice piece of template, and can even easily test whether our validations work correctly with unit tests, without diving into the view.
8. Global Error Handling
To catch synchronous errors in our code, we can use try-catch block. If error is thrown in our try block, we can catch it in catch block. But this is not an ideal situation to write try-catch everywhere. We need global error handling.
Angular provides a hook for centralized exception handling.
The default implementation of ErrorHandler prints error messages to the console
. To intercept error handling, write a custom exception handler that replaces this default as appropriate for your app.
|
|
9. Use HttpInterceptor to handle all http errors
It provides a way to intercept HTTP requests and responses to transform or handle them before passing them along.
There are two use cases that we can implement in the interceptor.
First, we can retry the HTTP call once or multiple times before we throw the error. In some cases, for example, if we get a timeout, we can continue without throwing the exception.
We can then check the status of the exception and see if it is a 401 unauthorized error. With token-based security, we can try to refresh the token. If this does not work, we can redirect the user to the login page.
|
|
We also need to provide the interceptor we created.
|
|
10. Perform multiple Http requests using ForkJoin
There are some use cases, when we need to perform multiple http requests at once, wait until all requests are completed and then proceed next just like synchronous http calls. So, Angular provides this feature with the help of ForkJoin.
ForkJoin
waits for each HTTP request to complete and group’s all the observables returned by each HTTP call into a single observable array and finally return that observable array.
|
|
The above example shows making three HTTP calls, but in a similar way, you can request as many HTTP calls as required.
|
|
As shown in the above code snippet, at the component level you subscribe to single observable array and save the responses separately.
Summary
In this blog, we have learned about best practices to create any angular app. These are really cool practices which helps you to write efficient and cool code.
Further Reading
How To Listen Changes In Reactive Form Controls Using valueChanges In Angular