Angular is a TypeScript-based free and open-source web application framework.
Best Practices of Angular Development


1. Avoid ‘any’ type
A type-checking language is Typescript. However, there may be other situations, such as when we require dynamic content or when we are unsure of the precise data type of some variables. In these circumstances, we can notify typescript that the variable can be of any type by using the data type 'any'.

But it's not a smart idea to always use "any." Any instructs the typescript to bypass type checking. This could put your code at risk of costly, hard-to-trace, and hard-to-debug problems.

const number_1: any = 1;
const number_2: any = 3;
const string_1: any = ‘a’;
const number_3: any = number_1 + number_2;
console.log(`Value of number_3 is:’, number_3);

// Output
Value of number_3 is 13


How we can overcome this by defining its type as mentioned below
const number_1: number = 1;
const number_2: number = 3;
const string_1: string = ‘a’;
const number_3: number = number_1 + number_2;
console.log(`Value of number_3 is:’, number_3);

// Output
Value of number_3 is 13


If we give the wrong type to any costant than we get the compile time error
const number_1: number = 1;
const string_1: number = ‘a’;
// This will give a compile error saying:
error TS2322: Type 'string'
is not assignable to type 'number'.
const string_1: number


2. Use Features of ES6
ES6 stands for ECMAScript 6, which offers new capabilities and syntax to create code that is more contemporary and understandable. It is always being updated with new features and functionality. JavaScript programming is made simpler with ES6 features like Let and Const, Arrow Functions, and string interpolation.

3. Use trackBy along with ngFor
An angular application that uses simply the *ngFor directive without the trackBy function will destroy all of the DOM elements and then reconstruct them in the DOM tree. Therefore, even when the same data is being used, having a lot of data will cause the programme to perform slowly. The trackBy and *ngFor directives should be used together because of this.

In Component.ts
trackByUserCode(index: number, user: any): string {
return user.code;
}

In Component.html

*ngFor='let user of users; trackBy:trackByUserCode'>


4. Use Lazy Loading
The process of loading various modules, such as documents, JS, CSS, videos, photos, etc., is known as lazy loading. By splitting the application's load time into several packets and loading them as needed, it accelerates application load time. Lazy loading can be helpful when loading a large application rather than utilising other functions to load the modules.

When anything is used, lazy loading only loads it. Instead of loading the component as specified in the AppRoutingModule routes settings, we can just use the lazy-loading function to loadChildren. One of the best angular practises is to lazy load or load a certain app feature when needed rather than when the app launches.
const routes: Routes = [{
path: 'users',
loadChildren: () => import('./modules/users/user.module').then(m => m.UserModule)
}];


5. Prevent Memory Leaks in Angular Observable
The first component is removed and the second component is initialised when we move from one component to the next. The initial component, which had subscribed to the Observable, has now been removed. This can result in memory leaks.

We can prevent this by following techniques

1. Using takeUntil()
TakeUntil monitors second Observables, and when the value of the observables is generated, it will dispose the subscription and the cycle will be complete.

this.authService.GetUserList()
  .pipe(takeUntil(this.ngUnsubscribe))
  .subscribe(res => {this.userList = res;});
...
...
ngOnDestroy() {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}


2. Using the Async pipe
It returns to the most recent value that was emitted after subscribing to an Observable or Promise.

*ngFor="let item of userService.GetUserList() | async";
{{item.userName}}


3. Using take(1)
It makes sure that you’re getting the data only once.

this.userService.GetUserList()
  .pipe(take(1))
  .subscribe(res = {this.userList = res;})


6. Avoid logic in templates
All template-related business logic can be isolated into a component even when the function calls in angular templates are technically correct. It helps with unit testing and also cuts down on issues in the event that a template change occurs in the future.

7. Declare Safe Datatypes
The first step will be to confirm the categories of data and the range of values it contains. Following that, the variable will only accept the possible values. We can declare a variable as a list of potential values or a different type instead of declaring it as a variable of a specific type.

Example
type Roles = "Admin" | "User";
const user1: Roles = "Admin";
const user2: Roles = "User";
const user3: Roles = "Teacher";


// The last line will give this error: Type ‘"Teacher"’ is not assignable to type 'Roles'.

8. User CDK Virtual Scroll
CDK (Component Development Kit) To present lengthy lists of components more effectively, utilise Virtual Scroll. By setting the container element's height to be the same as the sum of all the elements' heights, virtual scrolling makes it possible to simulate all values effectively.

9. Use Environment Variables
For all types of contexts, Angular provides various environment setups to declare individual variables. Angular environments for development and production are standard. We can even add new environments or variables to the environment files that already exist.

10. Use Lint Rules
The tslint provides a number of built-in parameters that can be customised in the tslint.json file, including no-any, no-console, no-magic-numbers, etc. The programme is forced to be better and more reliable as a result. You can configure it with your own lint rules and settings. This guarantees the clarity and coherence of our code.

11. Maintain proper folder structures
Before beginning any angular project, everyone should take into account the importance of creating and maintaining a correct folder structure. Throughout development, the folder structure should be adaptable to new developments.

12. Break large components into small reusable components.
It can also be seen as a development principle with a single responsibility. It is challenging to manage, test, and debug huge components. Reduce code duplication and make it simpler to manage, maintain, and debug the component by breaking it up into smaller, more reusable components if it grows in size.

13. State Management
One of the most challenging areas of software development is state management. By saving the state of any sort of data, Angular's state management facilitates the handling of state transitions.

NGRX, NGXS, Akita, and other state management libraries for Angular are available on the market, and each of them serves a different purpose. The ideal state management for our application can be chosen before it is put into use.

Some of the advantages of utilizing state management.

It allows data to be shared between multiple components.
It allows for centralized state transition control.
The code will be cleaner and easier to read.
It’s simple to troubleshoot when something goes wrong.
There are development tools that are available for tracing and debugging in state management libraries.

14. Documentation in code
Code documentation is a wise move. It will help a new developer to comprehend the project's logic and readability. A smart Angular practise is to document each variable and method.

The actual task that a method performs must be specified
/**
* This is the get function
* @param age This is the age parameter
* @returns returns a string version of age
*/
function getUserAge(age: number): string {
return age.toString();
}


in multi-line comments that accompany each method definition.

15. Single Responsibility Principle
Instead of grouping them all under a single ts file, it is preferable to make distinct ts files for components, templates, styles, and services. You can write clear, readable, and maintainable code if you treat each file as if it is responsible for a single functionality.

16. Using Interfaces
Whenever we are drafting a contract for our class, we must employ interfaces. By utilising them, we can compel the class to implement the attributes and functions stated in the interface. The best example of this is when your component has angular lifecycle hooks:

The HomeComponent class implements OnInit and OnDestroy.

Interfaces are the best technique to accurately describe the declared objects.

TypeScript will produce an error, enable IntelliSense for us, and begin populating an object if it does not include the properties of the interface:

export interface User {
id: number;
name: string;
email: string;
class: number;
gender: "male" | "female";
stream : "ECE" | "CSE" | "IT";
status: boolean;
}

17. Change Detection Optimisations.
Instead of hiding non-visible DOM components with CSS, it's a good idea to remove them from the DOM using *ngIf.
Transfer difficult computations into the ngDoCheck lifecycle hook to make your expressions faster.
Calculations that are complex should be cached for as long as possible.
Use the OnPush change detection method to tell Angular that no changes have been made. You are then able to bypass the entire change detection procedure.

18. Using Smart – Dumb components / Use Smart – Dumb Components Technique
By informing Angular that the dumb components have not changed, this solution makes it easier to employ the OnPush change detection mechanism. Smart components are employed for processing data through API calls, concentrating on functionality, and managing states. They are more concerned with how they appear than the dumb components, who are only interested in appearances.

I would greatly appreciate it if you would support me by subscribe the channel if have you enjoyed this post and found it useful. Thank you in advance.