Angular is not that easy. It requires deep learning. Building easy to read and maintain apps is no doubt an art. This article shares 5 ways to improve your Angular codebase quality. It includes everything from naming your file, complicated topics such as redux to state management. Learn how you can use all these tips for improving the way you code your Angular apps.
Let’s Begin!
1. Follow the Rules
People choose Angular over other frameworks for its rules. Angular app framework is clear about how things are to be done. This means that it comes with certain rules of its own which are to be followed to create a uniform code base across organization.
This approach is quite useful when working across cooperation borders. That’s because it helps the newcomers to gel into the team quickly due to familiarity with the code.
In other words, you need to follow Angular design guidelines to get the most out of its framework. This will not only add quality to your code but will also make your life a lot easier.
Given below are a set of rules which you may have already familiar with. Angular Style Guide
“We love to do things our way! We don’t want to follow someone else’s rules!”
If you don’t want to follow Angular’s rules, then you should not choose it as your front-end framework. A number of frameworks are available to suit your expectations. You won’t feel happy working with Angular.
Naming the Files
Naming Files is one example of some of the Angular’s rules you have to follow. Files in Angular have a very particular theme, also known as the naming convention. Every file containing an angular-structure, like a component, a pipe or a module is named in this way:
[name].[structure].[file-extension]
So, if you want to create a component to display to customers, name it “customer.” The structure would be a component and the file extension which is either “.ts”, “.css” or “.html”.
Custumer.component.ts
The Angular-cli takes care of all this stuff. It uses ng-generate command to create a structure. The file created as a result follows the naming convention. Check this tutorial to learn more about angular-cli.
2. Group Code into Modules
Placing everything into the app-module is common among developers and messes up everything. Try to avoid it and use modules.
Modules help to organize your code into small chunks. This makes it easy to read and find errors when troubleshooting it. In addition to the cosmetic advantage, you also get to increase the user-experience by downloading only those parts that require working.
Read a guide on modules to learn about modules if you are unfamiliar with them. However, don’t structure your modules the way you want. This would only make things worse. Luckily, Angular has defined some ways to help you structure your apps into modules.
Feature Modules
Feature Modules are one of the categories of available modules in the Angular framework. As the name gives it away, they are used to include one specific feature. These modules are created in another folder with the feature name.
For instance, the feature module for the feature “feature” is included into a directory named feature. This module follows the naming convention shared above: feature.module.ts.
Why do you need feature modules?
They structure our code in a way that makes it easy to understand and read. They also mark different features. This helps in overcoming any confusion or potential bugs that are otherwise caused due to overlapping.
Another benefit of the feature module is lazy loading. Lazy loading is a technique which helps in downloading only the required module to a client’s device. The other modules are not downloaded.
For instance, in case of an administrative section of a blog, it is unwise to serve that code to every user visiting that site.
This code is separated into the admin section and placed into a feature module. It is loaded with the help of lazy loading. When the user visits the site, he/she only downloads the code for the blog section when visiting the blog. The other JavaScript is only loaded when he/she visits other sections.
Core and Shared-Module
Feature modules encapsulate everything into a separate module. This way it wont be used in other parts of the application, without importing it. However, in some situations, it won’t make much sense.
Going back to the same example of the blog section, suppose we have to import the admin-module to use a simple utility-directive. This would make things quite confusing and also rule out the benefits of lazy loading. For this reason, Core and Shared modules are used.
Shared Modules
- Shared modules are used for pieces of your application that need to be used across several areas (features) of your application.
- If a component is going to be re-used in several features, then it will be considered as a shared module.
- Services and Pipes are usually stated as shared modules.
- Shared modules provide a way to share common pieces to fill out feature module “sections”.
A text-formatting module is a good example of a shared module. It contains a bunch of pipes to format text in specific manner.
This module is then used by all the feature modules without breaking the encapsulation of the other modules.
Core Module
The feature and shared modules are not enough for covering our requirements. We also require another module to place the once used app-wide services. These are encapsulated into CoreModule in a directory known as “core.”
We mention all our app-wide services used just once in this module. It is imported into the app-module.
This keeps our app-module nice and clean.
However, the core-module is not used only for services. Everything which is used app-wide but is not suitable for a shared module can be done in the core-module.
Loading spinners at the start of an app are a good example. They are not used anywhere in the app which is why creating an extra shared module is highly unsuitable for them.
Doing Private Services in Components
Usually the services in angular are provided on a global scope. But some are also provided at an application level. The global scope will only be helpful if the practice of the global-singleton-pattern is compulsory. For example if your service is responsible for storing things, you require one global instance. Otherwise, every component has its separate cache due to the scoped dependency injection in angular.
There are other services that do not need to be provided globally and are used by just one component. It’s better to provide that service inside of the component, instead of the service. Especially when that service is linked to that component.
Otherwise you would have to define the services in a module to make it accessible everywhere it may be needed.
This makes services related to features (feature-modules), which makes them easier to find and understand in the right context. This also enables benefits of lazy-loading capabilities. It also reduces the hazard of dead code.
3. Don’t Use Logic in Your Components
Keeping Logic outside your components is always a good idea. This also increases the quality of your code.
There are following reasons why you should keep your logic out of your components:
- Testing the user-interface and testing components testing is quite difficult in comparison to pure logic testing. This is why your business logic should be in a separate service.
- Secondly having your business logic in a separate service can help you write more effective tests efficiently and quickly. Other components can also use your logic, when placed separately as a service. It helps to reuse more of the code and consequently write less of it. Code that does not exist also increases the code-quality more.
- Last but not the least the code becomes easy to read when you have logic in a separate file.
State
If we talk about the state, there are a lot of challenges that arise from each component having its own state. It confuses you and makes you lose track of which component is in which state fast. That can make fixing errors quite difficult and results in errors that no one wants to have. This could be a big problem especially in large applications.
4. Make Sure Your Async Code is Correct
As discussed above angular is a framework with strict rules to achieve code-consistency. Same is the case with asynchronous code. The angular team uses the rxjs library for all asynchronous functions. The library makes use of the observer-pattern.
Avoid Promises
RxJs somewhat joins its functionality with the standard JavaScript promise. Both are predestined to handle asynchronous code but rxjs is far better. The purported rxjs-observables can resolve to more than just one value. This means that they are multiple values, you will have to see.
You can also pass only one result to that stream, which creates an overlap with the promise. One question comes to mind in this situation.
What should we use? The simple promise which allows us to use the TypeScript await operator? or should we use the powerful rxjs-observables? What if we use them both.
Here is my opinion;
I usually like the style of the await operator for promises, but according to my point of view, we should stick to the opinion of the framework. and that is to use rxjs everywhere.
We can see that, by observing at the angular HTTP-client, it yields rxjs-observables, even when it is clear, A HTTP-call can never give you output in more than one response.
Joining it up will not be a good solution. That way you get different implementations which are also not compatible with each other within the application. This is not something you would want to do.
Using the Async Pipe
As stated above, rxjs-observables are a little complicated. Using them incorrectly can lead to serious bugs.
The most communal mistake I make is forgetting to unsubscribe from the observable. This not only causes memory leaks, but also results in unwanted calculations and changes in your application.
public result; ngOnInit() { this.http.get('').subscribe(result => { this.result = result; }) }
But evading this mistake is easy in angular. You must use the angular async pipe. This pipe will inevitably unsubscribe from the observable, once the component is deleted.
public result$; ngOnInit() { this.result$ = this.http.get(''); }
and stick in the pattern to the observable using the async pipe:
<p>{{result$ | async}}</p>
This way the code looks simple and clean.
5. Use a Central State Management (Such as Redux)
As your app becomes larger, the code-quality can decline intensely. Hundreds of components, each having their own state not only become confusing but also becomes difficult to debug at the same time.
Centralized state management is the solution in all such conditions. What is a centralized state management? Centralized state management states that all of our application state is stored in one single location, instead of being dispersed all over the app. The overall state is controlled by one instance, that is the only one to make changes to the state. There are many advantages of this state management.
Centralized state management is the solution in all such conditions. What is a centralized state management? Centralized state management states that all of our application state is stored in one single location, instead of being dispersed all over the app. The overall state is controlled by one instance, that is the only one to make changes to the state. There are many advantages of this state management.
- You don’t have to search for it. As it is all in one place, you don’t need to search through the component tree.
- It’s easy to transfer between applications or limits the state to disk. It does not have to be obtained from several places, as it’s just one object.
- Problems like component to component communication are resolved by this, as well. They just react to state-changes.
- Based on which form of the central state management you select; you also get nice features like time-travel-debugging (Redux/ngrx).
Should Redux/ngrx be used?
Again, there are different opinions about this out there. Here is my point of view;
According to my personal view I don’t think that everyone should begin re-writing their apps to include redux. Even if you start from scratch, I don’t think redux needs to be used in most cases.
It totally depends on the kind of application you want to build. Here are different conditions;
- If you want to generate large applications with several components, developed by a large team then redux will be the best option.
- In case of medium sized-applications, not larger than the average app available on the app store, working with around 10 people, Redux must be avoided. That’s because it comes with a variety of boilerplate code which would unnecessarily complicate your app.
- It’s a big No in case of small apps.
Because in these medium and small size applications using redux would overcomplicate the code through its hundreds of boilerplate-files. I am not in favor of boilerplate code at all.
But there is a library that is under development and it provides zero boiler-plate code while working with redux and ngrx. Its called Angular-ngrx-data and is worth checking out.
Conclusion
I hope my 5 commendations on how to increase the quality of your angular code base will help you a lot.
Share this article with your friends and colleagues and help them become a better angular developer.
Good Luck!