Skip to content

5. Chapter 4 - Introduction to [Angular]

5.1. Sources

This chapter is based on the official documentation for [Angular]: angular.dev.

5.2. From AngularJS 1.x to [Angular]

The original document (2014) used AngularJS and 1.x: a framework (JavaScript—not yet TypeScript) organized around $scope, controllers, directives (ng-repeat, ng-if…), and a change detection mechanism based on the “digestion loop ” ($digest) and variable monitoring ($watch).

Starting in 2016, this framework was completely rewritten under the name [Angular] (without “JS”), as TypeScript, with a component-based architecture. Subsequent versions followed (2, 4… up to 22, used in this document), with two major changes you should be aware of before working with the [RdvMedecins] client code:

  • the phasing out of NgModules (@NgModule) in favor of standalone, self-contained components;
  • the introduction of signals (signal()), a new reactivity model that replaces $watch/$digest.

This chapter presents these concepts one by one, with short examples independent of the case study—the next chapter will demonstrate them in action within the complete [RdvMedecins] client.

5.3. The Concept of a [Angular] Application: the “Single Page Application”

A [Angular] application is a single-page application (SPA, SPA): the server delivers only a single page (HTML) to the browser, containing a single JavaScript file that embeds all of the application’s logic. Once this page is loaded, there is never another full page reload: every user interaction (changing views, submitting a form, etc.) is handled by the JavaScript code already present in the browser, which updates the display and communicates with the server in the background via asynchronous HTTP requests (most often using JSON). This was already the principle behind the original document with AngularJS and 1.x—it hasn’t changed.

5.4. Components

A component is the basic unit of a [Angular] application: it associates a view (a HTML template) with a TypeScript class that manages its state and behavior—exactly the role played, in the original document, the “view + C controller” pair in AngularJS 1.x, but here combined into a single entity.

Here is a very small component, independent of the case study, that could be created with ng generate component counter:

// compteur.component.ts
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-compteur',
  templateUrl: './compteur.component.html',
})
export class CompteurComponent {
  readonly valeur = signal(0);

  incrementer(): void {
    this.valeur.update((v) => v + 1);
  }
}
<!-- compteur.component.html -->
<p>Valeur : {{ valeur() }}</p>
<button (click)="incrementer()">+1</button>
  • line 4: [@Component]({...}) is a decorator (same mechanism as in [NestJS]; see Chapter 2) that declares the following class as a [Angular] component;
  • line 5: selector: 'app-compteur': the name of the custom HTML tag (<app-compteur>) which, when used in another component’s template, inserts that component at this location—the view composition mechanism, already present (in a different form, the directives) in AngularJS 1.x;
  • line 6: templateUrl: the HTML file associated with this component (you can also write HTML directly in the decorator, using the template property);
  • line 9: signal(0) creates a reactive value initialized to 0 (see the next section);
  • line 16: {{ value() }} in the template: the double curly braces display a value calculated by the class—syntax identical to that of AngularJS and 1.x, but note the parentheses: `value` is a signal, and therefore a function that must be called to read its current value;
  • line 17: (click)="incrementer()": the parentheses around `click` mean “listen for the `DOM` click event” and call the specified method—the exact equivalent of the `ng-click` directive in `AngularJS` 1.x.

5.5. Signals: The New Reactivity Model

In AngularJS 1.x, [Angular] detected data changes using a systematic check loop (the $digest), which iterated through all monitored expressions ($watch) at every possible interaction. This mechanism, while simple to use, became costly as an application grew.

A signal is a value container that knows when it changes and knows what depends on it (a template, another calculation, etc.)—so [Angular] no longer needs to recheck everything: only the parts of the application that actually depend on a modified signal are recalculated. Three functions are enough to grasp the basics:

Fonction
Rôle
monSignal(valeurInitiale)
creates a signal; its value is read by calling it as a function: monSignal()
monSignal.set(nouvelleValeur)
replaces the signal's value
monSignal.update(v => ...)
calculates the new value based on the old one (see increment() above)
computed(() => ...)
creates a signal that is automatically calculated from other signals and recalculated only when necessary

Two specialized variants of signal() are used to enable a component to communicate with its parent—they appear frequently in the [RdvMedecins] client (see the next chapter):

  • input() (and its variant input.required()) declares an input: a value provided by the parent component. This is the modern evolution of the historic @Input() decorator, and the conceptual equivalent of a AngularJS 1.x directive attribute;
  • output() declares an output: an event that the component can emit to its parent (using the .emit(value) method). This is the modern evolution of @Output() / EventEmitter, and the equivalent of the custom events ($emit) from AngularJS 1.x.

5.6. Standalone components

A AngularJS 1.x application was organized into modules (angular.module(...)), declaring controllers, directives, and services. [Angular] first adopted this idea in a more rigid form (the NgModule, @NgModule decorator), before introducing, in recent versions, standalone components—which have since become the default operating mode.

A standalone component declares everything it needs—such as the other components it uses in its template—within its own [@Component] decorator, rather than relying on a shared module:

1
2
3
4
5
6
@Component({
  selector: 'app-parent',
  imports: [CompteurComponent], // components used in the template
  templateUrl: './parent.component.html',
})
export class ParentComponent {}

This is the style used throughout the [RdvMedecins] client (see the next chapter): no *.module.ts files appear there anymore, a significant simplification compared to both AngularJS and 1.x, as well as to earlier versions of Angular.

5.7. Services and Dependency Injection

As in [NestJS] (Chapter 2), a [Angular] service is an ordinary class, decorated by [@Injectable], which [Angular] can automatically create and provide to any component that needs it - the same concept of dependency injection, for which AngularJS and 1.x were, in fact, among the very first JavaScript frameworks to popularize the principle (it is no coincidence that [NestJS] drew direct inspiration from it, as noted in Chapter 2).

1
2
3
4
5
6
7
8
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class MonService {
  saluer(): string {
    return 'Bonjour';
  }
}
  • Line 3: providedIn: 'root' indicates that a single instance of this service is created for the entire application (a singleton)—the equivalent of what a .service() or a .factory() was in AngularJS 1.x.

To obtain this service in a component, the modern [Angular] provides the inject() function, which replaces the legacy constructor injection (both still work, but inject() has become the standard practice, and is the one used by the [RdvMedecins] client):

1
2
3
4
5
6
7
import { Component, inject } from '@angular/core';
import { MonService } from './mon.service';

@Component({ ... })
export class MonComposant {
  private readonly monService = inject(MonService);
}

5.8. Communicating with the server: [HttpClient]

The [HttpClient] ([@angular/common/http]) module allows you to send requests HTTP - GET, POST… - to a server and process its response. It is a direct evolution of the $http service from AngularJS and 1.x used by the original document.


import { inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

const http = inject(HttpClient);
http.get<{ message: string }>('http://localhost:8080/hello').subscribe({
  next: (reponse) => console.log(reponse.message),
  error: (erreur) => console.error(erreur),
});

An important difference from $http: the methods of [HttpClient] (get, post…) do not directly return the response, nor a Promise as one might expect, but an Observable (RxJS library)—a stream of values over time, which you subscribe to using .subscribe({ next, error }) to be notified when the response arrives (or an error occurs). You can also transform an Observable before subscribing to it, using operators like map:

1
2
3
http.get<{ message: string }>('...').pipe(
  map((reponse) => reponse.message), // Only the "message" field is retained
);

The [RdvService] service of the [RdvMedecins] client (next chapter) consistently uses this .pipe(map(...)) combination for a specific reason explained there.

5.9. The new flow control syntax: [@if] and [@for]

AngularJS and 1.x used structural directives (ng-if, ng-repeat) to display an element conditionally or repeat a template block for each element in a list. [Angular] long used a very similar equivalent (*ngIf, *ngFor), before introducing, in its recent versions, a syntax integrated into the template language itself—the one used throughout the [RdvMedecins] client:

@if (utilisateurConnecte()) {
  <p>Bienvenue !</p>
} @else {
  <p>Veuillez vous connecter.</p>
}

<ul>
  @for (item of listeItems(); track item.id) {
    <li>{{ item.nom }}</li>
  }
</ul>
  • line 8: track item.[id] (required with [@for]) tells [Angular] how to recognize an element that has already been displayed when it redraws the list—much like how “track by” was already possible, as an option, with ng-repeat.

5.10. Summary: mappings AngularJS 1.x -> [Angular]

AngularJS 1.x (2014)
[Angular] (2026)
Contrôleur + $scope
Component (class + template)
$watch / boucle $digest
Signals (signal(), computed())
angular.module(...), @NgModule
Standalone components
@Input() / @Output() (historique)
input() / output() (signals)
.service() / .factory()
[@Injectable]({ providedIn: 'root' })
Injection par constructeur
inject() (or always via the constructor)
$http
[HttpClient] (Observables RxJS)
ng-if / ng-repeat
[@if] / [@for]
JavaScript
TypeScript

This background is sufficient to tackle the [RdvMedecins] client in the next chapter: each new feature will nevertheless be explained again there, as the code progresses, at the exact point where it appears.