AngularJS and the Origins of Angular

AngularJS made its debut in 2010, marking one of Google's initial forays into open-source front-end frameworks. The framework remained open source and enjoyed a 12-year lifespan before reaching its end-of-life milestone in 2022.

With AngularJS came a novel approach to writing HTML templates. Directives such as "ng-for" and "ng-if" introduced fresh concepts that would go on to influence numerous other frameworks and rendering engines that followed.

The AngularJS team soon recognized the challenges inherent in maintaining a substantial JavaScript codebase. This realization led to the announcement of Angular—a complete rewrite—in 2014.

Selecting the Right Tools

Angular has earned a reputation for thoroughly evaluating existing technologies before committing to the most suitable option. Here's a look at the tools the Angular team has adopted throughout the years.

Past Approaches

During the initial phases of Angular's development, the team opted for TypeScript—a language still in its infancy back in 2014, and one that drew criticism from many quarters. Some developers rejected the choice entirely, turning to alternative frameworks that permitted plain JavaScript.

Time has vindicated that decision, however. The advantages of TypeScript have become widely apparent, and today most developers actively prefer it over standard JavaScript.

Build tooling for JavaScript was just beginning to emerge in 2014. Tools like Grunt and Gulp were common at the time. Early Angular releases (2.0) relied on SystemJS, requiring developers to handle all configuration manually. With the arrival of Angular 4.0, the team introduced Webpack and the CLI, shielding developers from configuration concerns. Webpack proved remarkably reliable over the years, earning the trust of both developers and other frameworks.

At one point, the Angular team explored adopting Bazel as their build solution. While Bazel is a capable tool, its complexity presented significant challenges, and the team ultimately decided against pursuing that direction.

Current State

Angular continues to employ Webpack, while also providing experimental support for esbuild. The team recognizes the need for change and is evaluating alternatives that outperform Webpack.

The CLI has seen substantial refinement, particularly in easing version migrations. Visit https://update.angular.io/ for migration guidance.

Looking Forward

After assessing various build solutions, Angular will direct its investment toward esbuild for building applications.

You might wonder about vite, the current crowd favorite. The team examined it but determined it wasn't suitable as the primary build tool for Angular applications—though it will remain available for projects that use it during development builds.

Rendering Engines

Angular relies on a compiler to construct the final build output, which moves through several stages. The rendering engine is one such component, and the Angular team has refined it repeatedly. Ivy represents the current generation, opening the door to significant features like the Standalone component.

Reactivity Focus

Past

From its inception, Angular pushed developers toward reactive programming patterns. The framework adopted RxJs, the premier library available at the time. RxJs powered internal APIs including HTTP and Router, and even EventEmitter was built on an RxJs Subject.

Present

RxJs adoption has expanded considerably, with a growing developer base embracing this robust library. Yet, voice from the community has also pushed for Angular experiences that don't require RxJs, while others have requested deeper, more seamless RxJs integration.

Future

The Angular team is currently exploring Signals, with a concrete public RFC anticipated. Signals aim to flatten the learning curve that RxJs demands, while simultaneously exposing APIs that improve RxJs integration.

Change Detection

Past

Every framework must handle change detection carefully, and Angular is no exception. The team integrated zone.js in the early days to manage this process.

Present

Angular retains zone.js to this day, but performance challenges emerged for large-scale applications, forcing developers to pursue optimization strategies. The OnPush change detection strategy is now broadly adopted to boost performance. Libraries such as RxAngular provide ways to disable zone.js, albeit requiring some refactoring effort.

Future

The momentum is shifting toward letting developers prioritize code quality over change-detection mechanics. This is the deliberate philosophy Angular has embraced. With Signals on the horizon, zone.js will become optional, and concern over change-detection strategies will fade.

A Commitment to Inclusivity

Angular adheres to a six-month release cadence, meaning a new major version arrives twice a year. Each release carries an 18-month support window. If you remain on a given Angular version beyond that, support likely no longer exists.

Angular mitigates that risk, though. Every new release is accompanied by automatic migration tooling through the Angular CLI. A simple invocation of ng update handles the heavy lifting.

The documentation details how to update Angular across multiple versions.

ng update 
Enter fullscreen mode Exit fullscreen mode

A Rich Feature Set

Over time, the Angular team has deepened its engagement with the community, producing a series of excellent features.

The inject Function

Dependency Injection (DI) stands as one of Angular's most cherished capabilities, and the introduction of the inject function has further elevated the developer experience.

A comparison between the old and new approaches follows:

@Component({
  selector: 'app-employee-details',
  templateUrl: './employee-details.component.html',
  styleUrls: ['./employee-details.component.scss'],
})
export class EmployeeDetailsComponent {

  modes$ = this.route.queryParamMap.pipe(
    map((params: ParamMap) => params.get('mode'))
  );
  userName$ = this.route.paramMap.pipe(
    map((params: ParamMap) => params.get('username'))
  );

  employeeId$ = this.route.paramMap
    .pipe(
      map((params: ParamMap) => params.get('employeeId'))
    );
  constructor(private route: ActivatedRoute) { }

}
Enter fullscreen mode Exit fullscreen mode

Using the inject function

@Component({
  selector: 'app-employee-details',
  templateUrl: './employee-details.component.html',
  styleUrls: ['./employee-details.component.scss'],
})
export class EmployeeDetailsComponent {
  modes$ = inject(ActivatedRoute).queryParamMap.pipe(
    map((params: ParamMap) => params.get('mode'))
  );
  userName$ = inject(ActivatedRoute).paramMap.pipe(
    map((params: ParamMap) => params.get('username'))
  );

  employeeId$ = inject(ActivatedRoute).paramMap.pipe(
    map((params: ParamMap) => params.get('employeeId'))
  );
  constructor() {}
}
Enter fullscreen mode Exit fullscreen mode

Template:

<h1>
   Employee Details for EmployeeId: {{employeeId$ | async}}
</h1>

<h2>Current Mode: {{ modes$ | async }} </h2>
Enter fullscreen mode Exit fullscreen mode

Standalone components

The Angular community has long awaited a version of the framework that does not rely on Angular Modules. While modules still play a valuable role in application architecture, they are often unnecessary and add extra complexity to the learning process. The introduction of Standalone Components addresses this by allowing components to exist without module registration. These components remain fully compatible with the existing module-based system, so they can be integrated into an Angular Module whenever needed.

import { Component, OnInit } from '@angular/core';
import { MatTableModule } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.scss'],
  standalone: true, // Standalone flag to differentiate between component with module
  imports:[
    MatTableModule,
    MatPaginatorModule,
    MatSortModule
  ] // you can import Angular Module, Standalone Component/Directive/Pipe here
})
export class UserComponent implements OnInit{

  constructor() {
  }

}
Enter fullscreen mode Exit fullscreen mode

Standalone APIs

To further enhance the standalone component experience, Angular has rolled out Standalone APIs. Support for these APIs already exists in the Router and HttpClient, and external libraries such as NgRx have adopted the pattern as well.

Here is a code example showing how to utilize the Standalone APIs for HTTP and routing:


providers: [
    provideHttpClient(),
       provideRouter(
      routes,
      withDebugTracing(),
      withEnabledBlockingInitialNavigation() //required for SSR
      withHashLocation(),
      withPreloading( PreloadAllModules),
      withRouterConfig({
        onSameUrlNavigation: 'reload',
      })
    ),
]
Enter fullscreen mode Exit fullscreen mode

Standalone Component Migration

Considering the effort required to convert your entire codebase to standalone components? The Angular team has provided a straightforward solution. Running a single command will automatically migrate all your components, directives, and pipes to the standalone format. Happy migrating!

ng generate @angular/core:standalone
Enter fullscreen mode Exit fullscreen mode

Functional guards

Router guards have been a staple, but they previously required the boilerplate of a service class. The arrival of functional guards has changed that paradigm. Now, a guard can be written simply as a function, eliminating the need for a dedicated class.

const authGuard: CanMatchFn = () => {
  const authService = inject(LoginService);
  const router = inject(Router);

  if (authService.isLoggedIn) {
    return true;
  }

  return router.parseUrl('/login');
};
Enter fullscreen mode Exit fullscreen mode

New Image Directive

In 2023, images remain a major culprit behind poor LCP (Largest Contentful Paint) scores. With the NgOptimizedImage directive, this is no longer a concern you need to manage manually.

Optional ZoneJS

For any Angular developer, zone.js is a familiar name. It is the library that powers Angular's change detection magic.

However, in large enterprise applications—Angular's primary stomping ground—it begins to introduce noticeable performance overhead.

The good news is that zone will soon become optional for our applications. The introduction of Signals is making this a reality, and thanks to backward compatibility, you can keep your existing zone-based app running while adding these new signal-based components side by side. While there were previously ways to disable zone.js, none of them were very straightforward.

In the following example, the signals: true property is expected to land soon, but it is not yet available in version 16.0.0-next.7:

import { Component, signal } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-counter',
  standalone: true,
  signals: true, // you can still try this code by commenting this line
  imports: [CommonModule],
  templateUrl: './counter.component.html',
  styleUrls: ['./counter.component.scss']
})
export default class CounterComponent {
  count = signal(0);

  increment() {
    this.count.update(n => n + 1);
  }

}
Enter fullscreen mode Exit fullscreen mode
<p>counter works!</p>
{{ count() }}

<button (click)="increment()">Increment</button>
Enter fullscreen mode Exit fullscreen mode

Signals will bring benefits far beyond just making zone.js optional. You can learn more in the related RFCs:

https://github.com/angular/angular/discussions/49685

https://github.com/angular/angular/discussions/49684

https://github.com/angular/angular/discussions/49682

https://github.com/angular/angular/discussions/49683

https://github.com/angular/angular/discussions/49681

SSR and hydration

Server-Side Rendering in Angular has had its fair share of difficulties and has traditionally lagged behind SSR frameworks from the React or Vue ecosystems. To address this, the Angular team has begun a series of improvements, starting with the release of its first major feature.

Typed Forms

Angular offers two kinds of forms: Reactive Forms and Template-Driven Forms. The community had long requested type safety for these forms, and this feature finally arrived with Angular 14.

Indeed, you can now build typed forms, and you are likely already taking advantage of them.

  form: FormGroup = this.fb.group({
    name: new FormControl<string>(''),
    salary: new FormControl<number>(0),
    age: new FormControl<number>(0),
    dob: new FormControl<Date>(new Date()),
  });
constructor(private fb: FormBuilder) {}
Enter fullscreen mode Exit fullscreen mode

This article aimed to cover the exciting features that Angular has in store for developers. My perspective is optimistic about the future of investing in Angular, just as it was six years ago when I made the jump from a .Net developer to an Angular developer.

That investment of time in learning and supporting the Angular framework paid off in the past, continues to hold strong in the present, and with upcoming features like Signals, its future looks exceptionally bright.