Angular Best Practices - In this article, we are going to show what we consider to be the best practices in Angular while develop the client side project in this framework…

If you have been around in the last few years in the development field, I am sure you must have come across the name Angular at-least once. In this article, we are not only going to talk about what Angular is, but some of the best practices that a developer should keep in mind to keep the project efficient and the code easier to manage and debug.

What is Angular?

Angular is a modern MVVC framework and platform that is used to build enterprise Single-page Web Applications (or SPAs) using HTML and TypeScript. Angular is written in TypeScript. It implements core and optional functionality as a set of TypeScript libraries that you import into your apps. Angular is an opinionated framework which means that it specifies a certain style and certain rules that developers need to follow and adhere to while developing apps with Angular, therefore you need to learn Angular and the various components that make up an Angular app.

Angular vs AngularJS

A lot of beginners get confused with so many different versions out there for the same framework. You must have heard AngularJS, Angular 2, Angular 4, Angular 5, Angular 6 and now Angular 7. Well, in reality, there are two different frameworks - AngularJS and Angular.

AngularJS is the JavaScript-based web development framework which was first released in 2010 and is maintained by Google. Later, in September 2016, Angular 2 was announced which was a complete rewrite of the whole framework using TypeScript, a super-set language of JavaScript.

Since modern browsers (as of now) do not understand TypeScript, a TS compiler or transpiler is required to convert the TS code to regular JS code.

**Why Angular? **

Why do we use Angular that uses TypeScript as the primary programming language when it comes with the overhead of transpilation? Well, the answer lies in the list of advantages that TypeScript offers over traditional JavaScript. With TypeScript, developers can use data types, syntax highlighting, code completion all the other modern features that help them code faster and more efficient. Since TypeScript is an object oriented programming language, developers can use the advantages of classes, objects, inheritance and similar features that all other OOPLs offer.

Angular, therefore, is the framework that uses TypeScript as the primary programming language. Since the Angular team opted for semantic versioning, Angular 2, 4, 5, 6 and 7 are all the versions of the same framework, each version being better than the previous one, while AngularJS is a completely different framework that uses JavaScript as the primary programming language.

Recommended Best Practices

Next, we will go through some of the best practices that a developer should follow to keep the project easier to manage and debug. Although, there are no hard rules regarding these best practices, most developers follow them. You may have your own coding styles and that is completely fine too.

Using the power of TypeScript

Angular is written using TypeScript which means that all the code that you will be writing to build your amazing web-app will also be written using TypeScript. One key point to note here is that all the JavaScript code is valid TypeScript code. While it is recommended that you use TypeScript code all along, you can even you JavaScript code, if you will.

Using TypeScript offers you certain advantages as you are able to define types on the properties and variables, you can define classes & interfaces and utilize the power of interfaces and unions.

Specify types for all variables. This helps you prevent other developers working on the project from storing wrong information in the variable. For example, if a variable called age is of the type number, it can only store numerical values. If another developer writes the code to store the value of any other types, for example, a string, the code editor will warn the developer and the compilation process will also be able to spot the error in the code. These errors won’t leak into the runtime.

Classes in object oriented programming languages are used to contain data members and member functions related to a particular citizen in the application. Be it a product, a user or something else like that. Always create classes to represent these citizens in your applications. Encapsulate all the functionality related to these objects within these classes. This helps keep the consistent and maintainable when a large team is working on the project.

class User {
first_name: string;
last_name: string;
email: string;
constructor(f_name: string, l_name: string, email: string) {
this.first_name = f_name;
this.last_name = l_name;
this.email = email;
}
sendMail() {
// method to send an email to the user
}
}

Consistency brings productivity into the picture as well. Developers don’t have to worry as much about if they’re doing it the “right way”.

Create models to represent objects in your data that can be more complex than simply a string or a number. Using models allows you to use the help offered by the Code Editor you are using. Most errors that are likely to be introduced because of typing can be prevented because you do not need to remember the names of properties inside the model as the Code Editor will suggest the names as you type.

If you have worked with MVC frameworks before, you know that you declare types in a model which can then be reused throughout the rest of the application. With TypeScript, front-end applications can now benefit from strongly typed models. Consider a simple model for a user as below.

export interface User {
  name: string = 'Angular';
  age: number = 0;
}

According to the Angular Style Guide, models should be stored under a shared/ folder if they will be used in more than one part of your application.

A lot of times, developers depend on APIs and the data returned by the APIs. They retrieve the data, process it and present the data to the user in a nice and clean UI. It is possible that the APIs are not always perfect or some field of the API may not always contain the expected values or the values in the expected format.

TypeScript allows intersection types. This allows you to create variables of a type which is a combination of two or more types. Let’s have a look at an example.

interface Student {
    roll_number: number;
    name: string;
}
interface Teacher {
    teacher_id: string;
}
type A = Student & Teacher;
let x: A;
x.roll_number = 5;
x.name = 'Samarth Agarwal';
x.teacher_id = 'ID3241';

In the above code, the new type A is a combination of types both Student and Teacher and will therefore contain the properties of both types.

While intersection creates a new type with the combination of the provided types, union, on the other hand, allows you to have a type either of the several types provided as arguments. Let’s look at an example.

age: string | number;

In the code snippet above, the age variable can store a value of either the type string or number. So, both the following values will be fine.

this.age = 12;
this.age = 'twelve';

Use the Angular CLI

Angular CLI is one of the most powerful accessibility tools available when developing apps with Angular. The Angular CLI makes it easy to create an application that already works, right out of the box. It already follows all the best practices!

The Angular CLI is a command-line interface tool that is used to initialize, develop, scaffold, maintain and even test and debug Angular applications. You can use the tool directly in a command shell.

Instead of creating the files and folders manually, try to always use the Angular CLI to generate new components, directives, modules, services, pipes or even classes. Angular CLI updates the required module files and generates required files and folder. It also creates the required files for unit testing the components and directives. This maintains the uniformity of structure across the application and helps easily maintain, update and debug the project.

The CLI also allows you to use the development server to test the applications locally and then build the production build of the application for deployment.

To stay updated with the latest version of the CLI, you can use the following command.

npm install @angular/cli -g

You can also check the version installed on your system using the following command.

ng version

Naming Conventions

According to the Angular style guide, naming conventions are hugely important to maintainability and readability. Following general naming conventions are specified by the Angular style guide.

  • Do use consistent names for all symbols.
  • Do follow a pattern that describes the symbol’s feature then its type. The recommended pattern is feature.type.ts.
  • Do use dashes to separate words in the descriptive name.
  • Do use dots to separate the descriptive name from the type.
  • Do use conventional type names including .service, .component, .pipe, .module, and .directive. Invent additional type names if you must but take care not to create too many.

Why? Naming conventions help provide a consistent way to find content at a glance. Consistency within the project is vital. Consistency with a team is important. Consistency across a company provides tremendous efficiency.

Why? The naming conventions should simply help find desired code faster and make it easier to understand.

Why? Names of folders and files should clearly convey their intent. For example, app/heroes/hero-list.component.ts may contain a component that manages a list of heroes.

The purpose of the above guidelines is to ensure that just by looking at the filename, one should be able to infer the purpose and type of the contents of the file. For example, files with filenames hero.component.ts and hero.service.ts can be easily identified as being the files for the component and service for something called a hero in the project, respectively.

Note: If you are using the Angular CLI (which you always should), the file names will be taken care of automatically by the CLI.

Single Responsibility Principle

The single responsibility principle is a computer programming principle that states that every module, class, or function should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class.

Apply the single responsibility principle (SRP) to all components, services, and other symbols. This helps make the app cleaner, easier to read and maintain, and more testable.

According to the style guide, functions should be limited to 75 lines of code. Any method larger than that should be broken down into multiple files or methods. Each file, on the other hand, should be limited to 400 lines of code.

Creating one component per file makes it far easier to read and maintain the components as the application grows over time. This also helps avoid collisions with teams in source control. This also prevents any hidden bugs that may often arise when we combine multiple components in a file where they may end up sharing variables, creating unwanted closures, or unwanted coupling with dependencies. A single component can be the default export for its file which facilitates lazy loading with the router.

The key is to make the code more reusable, easier to read, and less mistake prone.

Breaking down Components

This might be an extension of the Single responsibility principle not just to the code files or the methods, but to components as well. The larger the component is, the harder it becomes to debug, maintain and test it. If a component is becoming large, break it down into multiple, more manageable, smaller components, dedicating each one to an atomic task.

In such a situation, if something goes wrong, you can easily spot the erroneous code and fix it spending less time in spotting the issue.

Look at the following code for a component called PostComponent which can be used to display various parts of the post including the title, body, author’s information and the comments made by people on the post.

<div>
<h1>
<post-title></post-title>
<h1>
<post-body></post-body>
<post-author></post-author>
<post-comments><post-comments>
</div>

The component contains multiple components and each of these components only handles a small task. We could have had one gigantic component instead of having 4 different ones but that would have been so hard to maintain and read.

It is also a good practice to keep minimal code within the component. Component should handle the job of displaying the data to the user in a nice and clean way while the responsibility of data retrieval should be outsourced to a service. The component should receive the data either as an input or use the service to retrieve the data.

Change Detection Optimizations

When you scaffold a brand new Angular application, the change detector seems magically fast. As soon as you change the value of a property on the click of a button or something like that, the view updates almost in real-time without any delays.

But, as the application grows, things may start to lag a bit. If you have drag & drop in your interface, you may find that you’re no longer getting silky-smooth 60FPS updates as you drag elements around.

At this point, there are three things you can do, and you should do all of them:

  • Use NgIf and not CSS - If DOM elements aren’t visible, instead of hiding them with CSS, is it a good practice to remove them from the DOM by using *ngIf.

  • Make your expressions faster. Move complex calculations into the ngDoCheck lifecycle hook, and refer to the calculated value in your view. Cache results to complex calculations as long as possible.

  • Use the OnPush change detection strategy to tell Angular there have been no changes. This lets you skip the entire change detection step on most of your application most of the time and prevents unexpected change detection steps when they are not required at all.

This saves the trouble to empirically determining all properties of all Angular Components and Directives for changes and therefore improves a lot on the performance of the application.

Build Reusable Components

It is a rule never said - build reusable components. If there is a piece of UI that you need in many places in your application, build a component out of it and use the component.

This will save you a lot of trouble if, for some reason, the UI has to be changed a little bit. In that case, you do not go around changing the UI code in all 100 places. Instead, you can change the code in the component and that is it. The changes will automatically be reflected in all the usages of the component throughout the application.

Reusing a component in multiple places may require the component to change itself based on the context and adjust accordingly. For this, you may have to use property and event bindings to pass inputs to the components and receive output events from the components, respectively.

API code in a Service

Components consume Services.

A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well. Service is a broad category encompassing any value, function, or feature that an app needs.

This again connects to the Single responsibility principle and keeping the components lean. In Angular, there is a clear line of separation between components and services, and this is done to increase modularity and reusability. When a component’s view-related functionality is separated from other kinds of processing, it makes the component classes lean and efficient.

Certain tasks can be delegated to services by components, such as fetching data from the server, validating user input, or logging directly to the console. If we define these tasks in a service, we are making this reusable across all the components in the application and more. However, Angular doesn’t enforce these principles but Angular does help you follow these principles by making it easy to break down your application logic into services and making those services available to components through dependency injection.

export class APIService {
  get() {
// code to get the data from the web service or API
}
  post(data: any) {
// code to send the data to the web service or API
}
  update(data: any) {
// code to update the data
}
}

The above code is a representation of a service that interacts with the external API on behalf of the application. This service can be used to get the data from the API, send the data to the API and update any existing data on the server. Other components use this service to handle sending and receiving of the data.

Using trackBy in NgFor

The NgFor directives is used to loop over a collection (or an array) in your application to render a piece of UI repeatedly. The following snippet is a typical implementation of rendering a collection using NgFor directive.

<ul>
<li *ngFor="let item of collection;">{{item.id}}</li>
</ul>
And let’s use the following code in the TS class to change the elements in the collection every 1 second.
import { Component } from '@angular/core';
@Component({
 selector: 'my-app',
 templateUrl: './app.component.html',
 styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
 collection = [{id: 1}, {id: 2}, {id: 3}];
 constructor() {
   setInterval(() => {
      let randomIndex = parseInt(((Math.random() * 10) % 3).toString());
      this.collection[randomIndex] = {
        id: parseInt(((Math.random() * 10) % 10).toString())
      }
    }, 1000)
 }

}

If we change the data in the collection, for example as a result of an API request or some other complex logic within the application, we may have a problem because Angular can’t keep track of items (not able to identify each individual element) in the collection and has no knowledge of which items have been removed or added or changed.

As a result, Angular needs to remove all the DOM elements that are associated with the data and create them again. That means a lot of DOM manipulations especially in case of a big collection, and as we know, DOM manipulations are expensive. This is okay if you have a small app or the collection only has a few elements but as the application grows, thus can cause performance issues in the application.

The solution is to always use trackBy whenever you use NgFor directive. Always ensure that you pass in a unique value to the trackBy so that Angular can uniquely identify each element in the collection using the unique value. In the following snippet, we use the id.

<ul>
<li *ngFor="let item of collection; trackBy: id">{{item.id}}</li>
</ul>

If you run the above code, and look at the DOM using Google Chrome Inspector, you will find that only the list item that has changed is re-rendered. All the other list items are unchanged.

The trackBy also takes a function as the argument which takes the index and the current item as arguments and needs to return the unique identifier for this item.

Lazy Loading Modules

Lazy loading is a technique in Angular that allows you to load JavaScript components asynchronously based of currently activated route. It is a feature that could help you a lot with large and heavy applications.

Since lazy loading breaks down the application into multiple modules (logical chunks of code) and loads those modules only when they are required by the user (depending on where the user navigates within the application), it reduces the initial load times of the application since less KBs are loaded when the application is loaded first. As the user navigates within the application, more chunks are loaded as and when required. Angular router has full support for lazy loading Angular modules.

Using Async Pipe

I always use async pipe when possible and only use .subscribe when the side effect is an absolute necessity.

You must have heard that the AsyncPipe unsubscribes from Observables as soon as the component gets destroyed. But did you also know it unsubscribes as soon as the reference of the expression changes?

That’s right, as soon as we assign a new Observable to the property that we use the AsyncPipe on, the AsyncPipe will automatically unsubscribe from the previous bound Observable. Not only does this make our code nice and clean, but it’s also protecting us from very subtle memory leaks.

Environment Variables

When we build projects using Angular (or any other technology for that matter), it’s common for developers to have multiple application versions that target different environments i.e. development and production. Each environment will have some unique environment variables i.e. API endpoints, app versions, datasets, etc. Angular provides environment configurations to declare variables unique for each environment.

By default angular supports two environments – production and development.

Inside the environment directory, there are two files, environment.ts and environment.prod.ts. While the first file contains the environment configuration and variables for the development environment, the second one contains the same for the production environment.

You can even add more environments, or add new variables in the existing environment files.

// environment.ts environment variables
export const environment = {
  production: false,
  api_endpoint_root: 'https://dev.endpoint.com'
};
// environment.prod.ts environment variables
export const environment = {
  production: true,
  api_endpoint_root: 'https://prod.endpoint.com'
};

Maintaining these variables helps a lot when something changes, for example, the API URL for the endpoint and when you build your application for a particular environment, the variables changes are applied automatically.

Always Document

Last, but not the least - Alway document your code as much as possible.

Writing comments within the code helps other developers involved in the project development a lot and understand the purpose and logic of the written code. It helps manage the code and adds to the readability of the code.

It is a good practice to document the use and role of each variable and method. For methods, each parameter should be defined using multi-line comments and it should also be defined what task exactly the method accomplishes.

What’s new?

We do not know much about the latest version of Angular, 8 (8.0.0-beta.8), yet but like any other major update, this version will be better, faster and result in a smaller build than the previous versions. It has improvements and fixes over the previous version of Angular.

#angular

Angular Best Practices
4 Likes74.70 GEEK