Angular

Best Practices 


1. Start Fresh with the right version

Several factors contribute to choosing the right angular version for your project. To decide on the right Angular version:

  1. Long-Term Support (LTS): Choose an LTS version for stability and extended support.
  2. Project Requirements: Ensure the version supports the features and performance needs of your project.
  3. Compatibility: Check compatibility with third-party libraries and tools.
  4. Community and Resources: Opt for a version with strong community support and available learning resources.

Choosing the right Angular version for your project can be a critical decision that impacts its success. With each new release, Angular introduces new features and enhancements, while also deprecating older ones.

2. Use Angular CLI

Leverage the Angular CLI (Command Line Interface) for creating projects, and generating components, services, modules, and more. The CLI enforces best practices, reduces human error, and provides a standardized approach to project setup.

The main benefits of using CLI are:

  • Saved time,
  • Platform independence,
  • Effortless configuration while adding new code.

Few examples of using angular CLI:

  1. ng new - This command in Angular CLI is used to create a new Angular project. This command sets up the initial project structure and installs the necessary dependencies.
  2. ng generate - To Generate components, routes, services and pipes with a simple command with test shells.
  3. ng serve - To test your app locally while developing.
  4. ng test - To run your unit tests or end-to-end tests
  5. ng lint - To run the linter tool on your Angular application. Linting helps ensure that your code adheres to specific style guidelines and best practices, catching potential errors and improving code quality.
  6. ng add @angular/pwa - To set up the Angular service worker

3.Environment Variables

1. Store environment variables in .env.development, .env.stage, and .env.production files.

2. All the .env files are strictly added in the GIT IGNORE file.

3. In NextJS, env variables beginning with NEXT_PUBLIC are available at client side as well. So before naming a variable with NEXT_PUBLIC make sure whether this env variable will be needed on the client side or not.

4. Have a clear Folder Structure

Adopt a modular approach by breaking your application into smaller, reusable modules. Each module should have a well-defined purpose and responsibility. This promotes code separation, reusability, and easier maintenance.

Here is a compliant folder and file structure:

 

 Root Directory

1.e2e/

  • Purpose: End-to-end testing folder.
  • Contains: Files and configurations for running end-to-end tests using Protractor.
  • Importance: Ensures that the entire application works correctly from a user's perspective.

2.node_modules/

  • Purpose: Dependency storage.
  • Contains: All npm packages and dependencies required by the project.
  • Importance: Essential for running, building, and developing the Angular application.

3.src/

  • Purpose: Main source code folder.
  • Contains: Application code, assets, environment settings, and configurations.

Detailed Breakdown:

app/

  • Purpose: Primary application code.
  • Contains: Components, services, modules, and other application-specific files.
  • Importance: Core of the application where the main logic resides.

assets/

  • Purpose: Static assets.
  • Contains: Images, fonts, and other static files.
  • Importance: Used for managing static resources.

environments/

  • Purpose: Environment-specific configurations.
  • Contains: Environment configuration files like environment.ts for development and environment.prod.ts for production.
  • Importance: Helps in managing different settings for different environments.

favicon.icoGlobal

  • Purpose: Website icon.
  • Importance: Displays the icon in the browser tab.

index.html

  • Purpose: Main HTML file.
  • Importance: Entry point for the web application. All Angular components get loaded into this file.

main.ts

  • Purpose: Main entry point for the Angular application.
  • Importance: Boots the Angular application by loading the root module.

polyfills.ts

  • Purpose: Polyfills for browser compatibility.
  • Importance: Ensures that the application works across different browsers.

styles.css

  • Purpose: Global styles.
  • Importance: Contains global styles that apply to the entire application.

test.ts

  • Purpose: Unit test configuration file.
  • Importance: Configures the Angular testing environment.

4.editorconfig

  • Purpose: Code style configuration.
  • Importance: Ensures consistent coding styles across different editors and IDEs.

5.gitignore

  • Purpose: Git ignore file.
  • Importance: Specifies which files and directories should be ignored by Git.

6.angular.json

  • Purpose: Angular workspace configuration.
  • Importance: Defines project configurations, build options, and other settings.

7.browserslist

  • Purpose: Browser compatibility configuration.
  • Importance: Specifies which browsers the project supports.

8.karma.conf.js

  • Purpose: Karma test runner configuration.
  • Importance: Configures the Karma test runner for unit tests.

9.package.json

  • Purpose: NPM configuration file.
  • Importance: Lists project dependencies, scripts, and other configurations.

10.README.md

  • Purpose: Project documentation.
  • Importance: Provides an overview of the project, instructions, and other relevant information.

11.tsconfig.app.json

  • Purpose: TypeScript configuration for the application.
  • Importance: Specifies TypeScript compiler options for the application code.

12.tsconfig.json

  • Purpose: General TypeScript configuration.
  • Importance: Base TypeScript configuration file.

13.tsconfig.spec.json

  • Purpose: TypeScript configuration for tests.
  • Importance: Specifies TypeScript compiler options for test files.

 

This structure helps in organizing an Angular project efficiently, promoting a clear separation of concerns and modularity. Each directory and file has a specific purpose, ensuring that the application is maintainable, scalable, and easy to understand.

 

5.Best Practices for Angular Routes

1. Modularize Routes

Feature Modules: Split your application into feature modules and configure routes for each module. This makes your application more maintainable and improves load times through lazy loading.

const routes: Routes = [ { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule) }, { path: 'profile', loadChildren: () => import('./profile/profile.module').then(m => m.ProfileModule) },]; 

2. Use Lazy Loading

Lazy Load Modules: Load feature modules only when they are needed, which reduces the initial load time of your application. const routes: Routes = [{ path: 'feature', loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule) }];

 

3.Default and Fallback Routes

Redirect to Default Route: Use a default route to redirect users to a specific path if no other path matches.

const routes: Routes = [ { path: '', redirectTo: '/home', pathMatch: 'full' }, { path: '**', redirectTo: '/not-found' }];

 

4.Route Guards

Protect Routes: Use route guards (CanActivate, CanDeactivate, CanLoad, Resolve) to control access and perform checks before activating a route.

const routes: Routes = [ { path: 'admin', component: AdminComponent, canActivate: [AuthGuard] }];

 

5.Prefetching Modules

Optimize Performance: Prefetch lazy-loaded modules to improve the user experience by reducing load times.

RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })

 

6.Parameterized Routes

Dynamic Routing: Use parameterized routes for paths that need to include dynamic values.

const routes: Routes = [ { path: 'user/:id', component: UserComponent }];

 

7.Nested Routes

Hierarchical Routing: Use nested routes for complex applications to represent parent-child relationships in views.

const routes: Routes = [ { path: 'products', component: ProductsComponent, children: [ { path: ':id', component: ProductDetailComponent } ]}];

 

8.RouterLink and RouterLinkActive

Template Navigation: Use routerLink for navigation and routerLinkActive to apply classes to the active links.

<a routerLink="/home" routerLinkActive="active">Home</a>

 

9.SEO and Accessibility

Meta Tags and Titles: Use Angular's Title and Meta services to manage page titles and meta tags for better SEO and accessibility.

const routes: Routes = [ { path: 'about', component: AboutComponent, data: { title: 'About Us' } }];

 

 

6. Use Lazy Loading

Lazy loading is a technique in Angular that allows for loading parts of the application on-demand, rather than loading everything at once. This can improve the performance of the application by reducing both the initial load time and the amount of memory required to run the application.

To use lazy loading in Angular, you can use the Angular Router. The router allows you to define lazy-loaded routes, which are only loaded when the user navigates to them. Here is an example of how to use lazy loading in Angular:

Create a new module for the lazy-loaded route. This module should have its own components, services and routes.

In the main routing module, import the new module and add it as a child route. Use the loadChildren property to specify the path to the new module’s file.

 

const routes: Routes = [  { path: 'lazy', loadChildren: './lazy/lazy.module#LazyModule' },  //other routes]; 

7. Prevent Memory Leaks in Angular Observable

Observable memory leaks are very common and found in every programming language, library, or framework. Observables in Angular are very useful as it streamline your data, but memory leak is one of the very serious issues that might occur if you are not focused. It can create the worst situation in mid of development. Here are some of the tips that follow to avoid leaks.

  • Using async pipe
  • Using take(1)
  • Using takeUntil()
  • Unsubscribe the subscriptions on ngOnDestroy to avoid memory leaks.

8. Break down and reuse your components

During Angular development, it’s easy to create big components with similar logic repeated many times throughout the application. But it’s a much better option to create small, reusable components.

The main benefits of breaking down and reusing your components are:

  • Easy maintenance because of small components,

  • Saved time because of the reusable components.

Below are few examples of reusable code:

  • Use pipes instead of repeated functions to manipulate block data.

  • Create global variables for all the static data.

  • Create dynamic common components for generic UI.

9. Use interfaces

It’s very likely you will operate on some data from the API. It usually has a predefined JSON format. It’s extremely good to have it typed with interfaces instead of using any. The interfaces in Angular allow specifying whether the property is mandatory (by default) or optional (you need to add ? after the property name).

Here’s a simple example:

export interface BookState { books: Book[]; loaded: boolean; error?: string | null;}

 

The main benefits of using interfaces are:

  • Fewer bugs,

  • Predictable data that’s easy to operate on.

10. Avoid ‘any’ type

Avoiding the use of ‘any’ type can potentially lower the number of unexpected issues. Also, not using any type in our application will make the refactoring uncomplicated. The problems can be restricted by typing the specific variables and keeping others away.

11. Use Reactive Forms

Prefer using reactive forms over template-driven forms for better control, validation, and testing. Reactive forms provide a more predictable approach to managing form state.

 

<!-- Avoid Doing --><input [(ngModel)]="username"><!-- Use Reactive Forms --><input [formControl]="usernameControl"> 

12. Keeping the Project and Dependencies Up to Date

Keeping the project and dependencies up to date is an important best practice for creating a new Angular project. Angular has a rapid release cycle, and staying current with the latest updates can help you take advantage of new features and fix security issues.

Use the command ng update in the terminal to check for updates for the current project, and ng update @angular/cli to check for updates for the Angular CLI.

It is also recommended to use a package manager like npm or Yarn to manage dependencies and keep them updated. Additionally, it is important to keep an eye on the project dependencies’ security advisories and update them as soon as possible if a vulnerability is discovered.

13. Best Practices for Commenting on Code

1. Use Angular's Built-in ProtectionsWrite Meaningful Comments:

  • Ensure that comments add value by explaining why something is done, not just what is done. Avoid stating the obvious.
// BAD Commenting: Increments the counter by 1counter++;//GOOD: Increment counter to track number of button clickscounter++;

2.Use Comments to Explain Complex Logic:

  • When the code involves complex logic or algorithms, provide detailed explanations to clarify the thought process.
// Calculate the factorial of a number using recursionfunction factorial(n: number): number { // Base case: factorial of 0 is 1 if (n === 0) { return 1; } // Recursive case: n * factorial of (n-1) return n * factorial(n - 1);}

3.Keep Comments Up-to-Date:

  • Ensure comments are updated whenever the corresponding code is modified. Outdated comments can be misleading.

4.Use JSDoc for Function and Class Documentation:

  • Use JSDoc comments to document functions, methods, and classes. This provides structured and easily understandable documentation.
/** * Calculates the sum of two numbers. * @param {number} a - The first number. * @param {number} b - The second number. * @returns {number} The sum of the two numbers. */function add(a: number, b: number): number { return a + b;}

 

5.Comment Out Debugging Information:

  • Temporarily comment out code that is used for debugging or testing, and explain why it is commented out.
// Uncomment the following line to log the value of counter for debuggingconsole.log('Counter value:', counter);

 

By following these best practices, you can ensure that your Angular code is well-documented, making it easier for yourself and others to work with it effectively

 

14. Unit Testing After Development

Comprehensive Unit Testing:

  • Description: Ensure that all functions and modules are thoroughly tested to validate their behavior and correctness.

  • Example: Create unit tests for all possible edge cases, such as empty inputs, large numbers, and invalid data types.

Impact Analysis:

  • Description: Assess how changes in the code impact other parts of the application to prevent unintended consequences.

  • Example: Use tools like code coverage reports and dependency graphs to identify affected areas.

Code Refactoring:

  • Description: Refactor code to improve its structure, readability, and maintainability without altering its functionality.

  • Example: Simplify complex functions, remove duplicate code, and adhere to coding standards.

Optimization:

  • Description: Optimize code for performance, ensuring it runs efficiently and meets performance requirements.

  • Example: Profile your application to identify bottlenecks and optimize critical sections, such as database queries and loops.

Regression Testing:

  • Description: Conduct regression tests to ensure that new changes do not break existing functionality.

  • Example: Re-run existing test suites to verify that previous features still work as expected after new changes.

Documentation:

  • Description: Update documentation to reflect new changes, ensuring that future developers can understand and maintain the code.

  • Example: Add proper comments in the code. 

0