Unleash the Power of Angular: Hidden Features You've Probably Never Used
Tags: angular, tutorial, javascript, typescript
Published: 2023-09-08 07:38:08


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/byrhgbkrs6cchl9uq106.png)
 

## Introduction

Angular is a powerful and versatile front-end framework known for its robust set of features that enable developers to build dynamic and responsive web applications. While many developers are familiar with Angular's core concepts and commonly used features, there are several hidden gems within Angular that often go unnoticed. In this comprehensive guide, we'll explore some of these lesser-known features with full examples that can significantly enhance your Angular development experience.

## Angular Features Beyond the Basics

### 1. **Angular CLI Aliases**

Angular CLI (Command Line Interface) is a handy tool for generating components, services, and modules. However, did you know that you can create custom aliases for commonly used commands? This can greatly simplify your workflow.

#### Example:

Suppose you want to create a new component named `my-component`, but you'd like to use a shorter alias `myc` for this command. You can do this as follows:

```shell
ng generate component my-component --alias=myc
```

Now, you can generate the component using the alias:

```shell
ng g myc
```

This not only saves you time but also makes your CLI commands more concise and readable.

### 2. **Angular Elements**

Angular Elements allow you to package Angular components as custom elements (web components) that can be used in non-Angular applications. This feature facilitates component reuse across different frameworks or even vanilla HTML.

#### Example:

Let's create a simple Angular component and export it as a custom element:

```typescript
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-greet',
  template: '<h1>Hello, {{ name }}!</h1>',
})
export class GreetComponent {
  @Input() name: string;
}
```

Next, let's create a custom element from this component:

```typescript
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { GreetComponent } from './greet.component';

@NgModule({
  declarations: [GreetComponent],
  entryComponents: [GreetComponent],
})
export class AppModule {
  constructor(private injector: Injector) {
    const greetElement = createCustomElement(GreetComponent, { injector });
    customElements.define('app-greet', greetElement);
  }
  ngDoBootstrap() {}
}
```

Now, you can use the `app-greet` element in any HTML file:

```html
<app-greet name="Angular"></app-greet>
```

### 3. **Dynamic Component Loading**

Angular provides a powerful way to load components dynamically at runtime using the `ComponentFactoryResolver`. This is especially useful for scenarios where you need to load components conditionally or based on user interactions.

#### Example:

Let's create a simple Angular app that loads a component dynamically when a button is clicked:

```typescript
import { Component, ComponentFactoryResolver, ViewChild, ViewContainerRef } from '@angular/core';
import { DynamicComponent } from './dynamic.component';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="loadDynamicComponent()">Load Dynamic Component</button>
    <ng-container #dynamicComponentContainer></ng-container>
  `,
})
export class AppComponent {
  @ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) container: ViewContainerRef;

  constructor(private resolver: ComponentFactoryResolver) {}

  loadDynamicComponent() {
    // Dynamically load the DynamicComponent
    const factory = this.resolver.resolveComponentFactory(DynamicComponent);
    const componentRef = factory.create(this.container.parentInjector);
    this.container.insert(componentRef.hostView);
  }
}
```

In this example, when the "Load Dynamic Component" button is clicked, the `DynamicComponent` is loaded dynamically and displayed in the `dynamicComponentContainer`.

### 4. **Dependency Injection Scopes**

Angular's dependency injection system supports different injection scopes, including `root`, `platform`, and `any`. Understanding these scopes can help you manage your services and their lifecycle effectively.

#### Example:

Consider a service that should have a single instance shared across the entire application. You can achieve this by providing the service at the root level:

```typescript
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class MyService {
  // Service logic here
}
```

### 5. **ng-template and ng-container**

The `ng-template` and `ng-container` directives are versatile tools for rendering content conditionally and structuring your templates efficiently. They are invaluable when building complex views.

#### Example:

Suppose you want to conditionally render content based on a boolean variable `showContent`. You can use `ng-template` and `ng-container` as follows:

```html
<ng-container *ngIf="showContent; else noContent">
  <!-- Content to be displayed when showContent is true -->
  <p>This content is visible.</p>
</ng-container>

<ng-template #noContent>
  <!-- Content to be displayed when showContent is false -->
  <p>No content to display.</p>
</ng-template>
```

### 6. **TrackBy Function for ngFor**

When using `ngFor` to loop through a list, you can improve performance by providing a `trackBy` function. This function helps Angular identify which items have changed in the list and only update those, reducing unnecessary re-rendering.

#### Example:

Suppose you have a list of items and you're using `ngFor` to display them. To improve performance, add a `trackBy` function that uses a unique identifier (e.g., `id`) to track changes:

```typescript
@Component({
  selector: 'app-item-list',
  template: `
    <ul>
      <li *ngFor="let item of items; trackBy: trackByItemId">{{ item.name }}</li>
    </ul>
  `,
})
export class ItemListComponent {
  items: Item[] = [
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' },
    { id: 3, name: 'Item 3' },
  ];

  trackByItemId(index: number, item: Item): number {
    return item.id;
  }
}
```

By using the `trackBy` function, Angular can efficiently update the list when changes occur.

### 7. **ViewChild and ContentChild**

While `@ViewChild` and `@ContentChild` are well-known, not everyone fully explores their capabilities. These decorators allow you to access child components and elements within your templates dynamically.

#### Example:

Suppose you have a parent component with a child component and you want to access a property or method of the child component from the parent. You can use `@ViewChild` to achieve this:

```typescript
import { Component, ViewChild } from '@angular/core';
import { ChildComponent } from './child.component';

@Component({
  selector: 'app-parent',
  template: `
    <app-child></app-child>
    <button (click)="callChildMethod()">Call Child Method</button>
  `,
})
export class ParentComponent {
  @ViewChild(ChildComponent) childComponent: ChildComponent;

  callChildMethod() {
    this.childComponent.doSomething();
  }
}
```

In this example, `@ViewChild` allows you to access the `ChildComponent`

 instance and call its `doSomething` method.

### 8. **Router Guards and Resolvers**

Angular's router provides powerful guard and resolver features that allow you to control access to routes and pre-fetch data before a route is activated. Leveraging these can lead to more secure and efficient navigation in your application.

#### Example:

Suppose you have a route that requires authentication before it can be accessed. You can use a route guard to enforce this:

```typescript
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean {
    if (this.authService.isAuthenticated()) {
      return true;
    } else {
      this.router.navigate(['/login']);
      return false;
    }
  }
}
```

In this example, the `AuthGuard` checks if the user is authenticated before allowing access to the route.

### 9. **i18n Internationalization**

Angular's built-in internationalization (i18n) support allows you to easily create multi-language applications. You can mark text for translation directly in your templates and generate translation files.

#### Example:

Suppose you want to internationalize your application. You can use the `i18n` attribute to mark translatable text in your templates:

```html
<h1 i18n="@@welcomeHeader">Welcome to our app!</h1>
<p i18n="@@welcomeText">This is a sample application.</p>
```

Then, use the Angular CLI to extract these messages into translation files:

```shell
ng xi18n
```

This generates `.xlf` files that can be translated into different languages.

### 10. **Web Workers Integration**

Angular can work with web workers, which are separate threads for running JavaScript code. This feature enables you to offload heavy computations to a different thread, improving application responsiveness.

#### Example:

Suppose you have a computationally intensive task, such as calculating Fibonacci numbers. You can move this task to a web worker to avoid blocking the main thread:

```typescript
// fibonacci.worker.ts
addEventListener('message', ({ data }) => {
  const n = data;
  const result = calculateFibonacci(n);
  postMessage(result);
});

function calculateFibonacci(n) {
  if (n <= 1) {
    return n;
  }
  return calculateFibonacci(n - 1) + calculateFibonacci(n - 2);
}
```

In your main application, you can create and communicate with the web worker:

```typescript
const worker = new Worker('./fibonacci.worker', { type: 'module' });
worker.onmessage = ({ data }) => {
  console.log(`Fibonacci result: ${data}`);
};

// Start the computation
worker.postMessage(10); // Calculate Fibonacci for 10
```

By using web workers, you can ensure that intensive computations do not affect your application's responsiveness.

## Frequently Asked Questions

### Q1: What are Angular CLI aliases, and how can they improve my workflow?

**A1:** Angular CLI aliases are custom shortcuts for commonly used CLI commands. They save time and keystrokes when generating components, services, or modules by allowing you to define your own aliases.

### Q2: Can you explain more about dynamic component loading in Angular?

**A2:** Dynamic component loading involves using `ComponentFactoryResolver` to load components at runtime. It's especially useful for scenarios where you want to create components conditionally or based on user interactions.

### Q3: How does the trackBy function optimize ngFor loops?

**A3:** The `trackBy` function improves the performance of `ngFor` loops by helping Angular identify which items have changed in a list. This way, Angular only updates the elements that require changes, reducing unnecessary rendering.

### Q4: What are Angular Router Guards and Resolvers?

**A4:** Angular Router Guards are used to control access to routes by defining logic that determines if a route can be activated. Resolvers are used to fetch data before a route is activated, ensuring that the required data is available when the route is loaded.

## Conclusion

Angular is a feature-rich framework that offers more than meets the eye. By exploring these lesser-known features with full examples, you can become a more proficient Angular developer and build applications that are not only powerful but also efficient and maintainable. From Angular CLI aliases to dynamic component loading and advanced dependency injection, these features have the potential to take your Angular development to the next level. So, don't limit yourself to just the basics—dive deeper into Angular's hidden treasures and unlock its full potential for your next project.