Préparation d'entretien 100% Gratuit angularforall.com

Entretien Angular Junior — Signals, RxJS et @defer

Angular Junior Quiz-Angular-Junior Angular-Signals Computed Effect Angular-Defer Lazy-Loading Rxjs Switchmap Combinelatest Qcm-Angular-Junior Reactive-Forms Angular-17
Junior 🔀 Mixte 20 questions ⏱ 15 min
📝

Entretien Angular Junior — Signals, RxJS et @defer

20 questions Angular 17 niveau intermédiaire : signals (computed, effect, toSignal), blocs @defer, lazy loading standalone, opérateurs RxJS (switchMap, combineLatest), intercepteurs HTTP et reactive forms.

20 questions ⏱ ~15 min Niveau Junior

Partager

Banque de révision : les 60 questions corrigées

Voici l'intégralité des 60 questions de « Entretien Angular Junior — Signals, RxJS et @defer », chacune avec sa bonne réponse et son explication. Le quiz interactif ci-dessus en tire une sélection aléatoire à chaque tentative — utilisez cette liste complète pour réviser.

  • Les composants standalone n'ont pas besoin d'être déclarés dans un module, ils importent directement leurs dépendances (bonne réponse)
  • Les applications standalone ne peuvent pas utiliser de services
  • NgModule est obligatoire dans Angular 17+
  • Il n'y a aucune différence
Explication : Depuis Angular 17, les composants standalone sont la recommandation officielle. Un composant standalone définit standalone: true et importe directement les directives, pipes et autres composants dont il a besoin via imports: [], sans passer par un NgModule.
Les nouvelles applications créées avec ng new utilisent les composants standalone par défaut.

  • Uniquement via le constructeur : constructor(private service: MonService) {}
  • Uniquement via la fonction inject() : private service = inject(MonService)
  • Les deux méthodes sont valides (bonne réponse)
  • Aucune, les services ne sont pas disponibles dans les composants standalone
Explication : Les deux méthodes fonctionnent. La fonction inject() (introduite dans Angular 14) est plus flexible car elle peut être utilisée dans des fonctions utilitaires ou des guards, pas seulement dans les constructeurs.
La fonction inject() est souvent préférée dans les composants standalone car elle réduit le boilerplate.

  • @for (item of items; track item.id) { {{ item.name }} } (bonne réponse)
  • *ngFor="let item of items; trackBy: trackById"
  • @iterate (item of items) { {{ item.name }} }
  • {{ item.name }}
Explication : La nouvelle syntaxe @for nécessite un paramètre track obligatoire pour optimiser le rendu : @for (item of items; track item.id) { ... }. On peut aussi utiliser @empty pour afficher un contenu quand le tableau est vide.
Contrairement à *ngFortrackBy était optionnel, track est maintenant obligatoire pour de meilleures performances.

  • Utiliser @empty après @for (bonne réponse)
  • Utiliser *ngIf="items.length === 0"
  • Utiliser un pipe personnalisé
  • Utiliser @placeholder
Explication : La nouvelle syntaxe @for permet d'ajouter un bloc @empty qui s'affiche quand le tableau itéré est vide. Exemple : @for (item of items; track item.id) { ... } @empty { <p>Aucun élément</p> }.
Cette approche est plus lisible que d'utiliser *ngIf séparément pour vérifier si le tableau est vide.

  • Différer le chargement d'une partie du template jusqu'à ce qu'une condition soit remplie (bonne réponse)
  • Retarder l'exécution d'une fonction
  • Mettre en pause la détection de changement
  • Désactiver un composant temporairement
Explication : Le bloc @defer permet de charger paresseusement (lazy loading) des parties du template. On peut le déclencher avec différents événements : on viewport, on interaction, on idle, on timer, ou on hover.
Exemple : @defer (on viewport) { <composant-lourd /> } @placeholder { <div>Chargement...</div> }

  • @defer (on interaction(button)) (bonne réponse)
  • @defer (on click)
  • @defer (trigger button)
  • @defer (on event click)
Explication : La syntaxe correcte est @defer (on interaction(button)) où "button" est une référence de template (ex: #button). On peut aussi utiliser on interaction sans paramètre pour n'importe quelle interaction.
Autres déclencheurs : on viewport (visible à l'écran), on idle (quand le navigateur est inactif), on timer(5s) (après 5 secondes).

  • Un mécanisme plus rapide qui ne vérifie que les zones modifiées sans parcourir tout l'arbre des composants (bonne réponse)
  • La suppression complète de la détection de changement
  • Un remplacement de Zone.js par une API native
  • L'obligation d'utiliser OnPush pour tous les composants
Explication : Angular 17 a introduit des améliorations significatives de la détection de changement, notamment un algorithme plus intelligent qui ne vérifie que les parties de l'arbre des composants potentiellement affectées par un changement, améliorant ainsi les performances.
Ces améliorations sont automatiques, vous n'avez rien à changer dans votre code pour en bénéficier.

  • loadComponent: () => import("./mon.component") (bonne réponse)
  • component: () => MonComponent
  • lazy: MonComponent
  • loadChildren: () => import("./mon.component")
Explication : Avec les composants standalone, on utilise loadComponent dans la configuration de la route : { path: "route", loadComponent: () => import("./mon.component").then(m => m.MonComponent) }.
Cette approche remplace loadChildren qui était utilisée pour les modules. Le lazy loading est automatique : le composant n'est chargé que quand la route est activée.

  • @if est la nouvelle syntaxe de bloc, plus performante et supporte @else if et @else directement (bonne réponse)
  • Ils sont strictement identiques
  • @if ne supporte pas le else
  • *ngIf est plus rapide que @if
Explication : La nouvelle syntaxe @if utilise des blocs et supporte nativement @else if et @else, ce qui la rend plus lisible que *ngIf avec ses templates ng-template. Elle est également plus performante.
Exemple : @if (score > 90) { Excellent } @else if (score > 70) { Bien } @else { À améliorer }

  • @for (item of items | filter; track item.id)
  • @for (item of items; track item.id) { {{ item.name | uppercase }} } (bonne réponse)
  • Les pipes ne sont pas compatibles avec @for
  • @for (item in items) avec pipe dans la valeur
Explication : Les pipes s'utilisent normalement dans les expressions à l'intérieur des blocs @for. Cependant, il n'est pas recommandé d'utiliser des pipes dans la déclaration @for (item of items | filter) car cela pourrait causer des problèmes de performance.
Pour filtrer des données, préférez le faire dans le composant (via un getter ou un signal) plutôt que dans le template.

  • Un système de réactivité granulaire introduit dans Angular 16, amélioré dans Angular 17 (bonne réponse)
  • Un nouveau type de service
  • Une alternative aux Observables uniquement pour les formulaires
  • Une fonction de débogage
Explication : Les signals (introduits dans Angular 16, maturité dans Angular 17) sont un système de réactivité granulaire qui permet à Angular de savoir précisément quelles parties du template dépendent de quelles données, optimisant ainsi la détection de changement.
Un signal se crée avec signal(valeurInitiale), se lit avec monSignal(), et se modifie avec monSignal.set() ou monSignal.update().

  • count = signal(0); utiliser {{ count() }} dans le template (bonne réponse)
  • count = new Signal(0); utiliser {{ count }}
  • @Signal count = 0; utiliser {{ count }}
  • this.signal(0); utiliser {{ this.signal }}
Explication : Un signal se crée avec la fonction signal() importée depuis @angular/core. Dans le template, on appelle le signal comme une fonction : {{ count() }}. Angular détecte automatiquement les changements du signal.
Pour créer un signal dérivé (computed signal), utilisez computed(() => count() * 2).

  • Les signals permettent des mises à jour plus précises sans vérifier tout l'arbre des composants (bonne réponse)
  • Les signals remplacent complètement Zone.js
  • Les signals sont plus lents mais plus faciles à utiliser
  • Il n'y a aucun avantage
Explication : Avec Zone.js, Angular doit vérifier tout l'arbre des composants quand quelque chose peut avoir changé. Avec les signals, Angular sait exactement quels composants dépendent de quelles données, permettant des mises à jour beaucoup plus ciblées et performantes.
Angular 17 continue de supporter Zone.js, mais les signals sont l'avenir de la réactivité dans Angular.

  • monSignal.set(nouvelleValeur) ou monSignal.update(prev => prev + 1) (bonne réponse)
  • monSignal = nouvelleValeur
  • monSignal.value = nouvelleValeur
  • updateSignal(monSignal, nouvelleValeur)
Explication : Un signal se modifie avec .set() pour une nouvelle valeur, ou .update() pour une mise à jour basée sur la valeur précédente. update reçoit une fonction qui prend la valeur actuelle et retourne la nouvelle valeur.
update(prev => prev + 1) est particulièrement utile pour les compteurs ou les accumulateurs.

  • Un signal dont la valeur est calculée à partir d'autres signals et se met à jour automatiquement (bonne réponse)
  • Un signal qui ne peut être lu qu'une seule fois
  • Un signal utilisé uniquement pour les calculs mathématiques
  • Un signal qui remplace les Observables
Explication : Un computed signal se crée avec computed(() => ...). Il dépend d'autres signals et se recalcule automatiquement quand ses dépendances changent. Il est en lecture seule et ne peut pas être modifié directement.
Exemple : fullName = computed(() => `${firstName()} ${lastName()}`);

  • toSignal(monObservable$) (bonne réponse)
  • monObservable$.toSignal()
  • signalFrom(monObservable$)
  • observableToSignal(monObservable$)
Explication : La fonction toSignal() (importée de @angular/core/rxjs-interop) convertit un Observable en signal. Elle peut prendre un objet d'options comme initialValue ou requireSync.
N'oubliez pas d'importer toSignal depuis @angular/core/rxjs-interop : import { toSignal } from "@angular/core/rxjs-interop";

  • Aucun changement, on utilise toujours (event)="handler()" (bonne réponse)
  • On utilise @Output() avec signal
  • On utilise bind-event
  • On ne peut plus utiliser @Output avec les composants standalone
Explication : La syntaxe des événements n'a pas changé avec les composants standalone. On continue d'utiliser (nomEvenement)="handler($event)" dans le template parent. Le composant enfant définit toujours @Output() nomEvenement = new EventEmitter<Type>().
Vous pouvez maintenant aussi utiliser des outputs comme des signaux avec la nouvelle API output() (expérimental dans Angular 17).

  • Dans le tableau imports: [FormsModule] du décorateur @Component (bonne réponse)
  • Dans le tableau providers du composant
  • Dans le fichier app.config.ts uniquement
  • Les composants standalone ne peuvent pas utiliser FormsModule
Explication : Les composants standalone importent leurs dépendances directement dans le tableau imports: [] du décorateur @Component. Exemple : @Component({ standalone: true, imports: [FormsModule, CommonModule] }).
Le CommonModule est souvent importé pour avoir accès aux directives ngIf, ngFor, etc., bien qu'avec la nouvelle syntaxe, vous n'en ayez plus besoin.

  • Le processus qui rend une application rendue côté serveur interactive côté client (bonne réponse)
  • Le chargement des polices
  • La mise en cache des données
  • L'optimisation des images
Explication : L'hydration (ou réhydratation) est le processus par lequel Angular prend le HTML généré côté serveur et le rend interactif côté client en attachant les gestionnaires d'événements et en initialisant les composants.
Angular 17 a amélioré l'hydration avec une meilleure gestion des erreurs et des performances accrues.

  • ng generate service mon-service (bonne réponse)
  • ng create service mon-service
  • ng add service mon-service
  • ng new service mon-service
Explication : La commande ng generate service mon-service (ou ng g s mon-service) crée un nouveau service. Elle génère un fichier mon-service.service.ts avec un décorateur @Injectable({ providedIn: "root" }).
Utilisez ng g s services/mon-service pour créer le service dans un dossier spécifique.

  • Utiliser un guard canActivate avec inject() et le routeur (bonne réponse)
  • Utiliser *ngIf dans le template de la route
  • Utiliser un middleware comme dans Express
  • Vérifier l'authentification dans ngOnInit du composant
Explication : La meilleure pratique est d'utiliser un guard canActivate. Avec Angular 17+, les guards peuvent être des fonctions simples : canActivate: [() => inject(AuthService).isLoggedIn() || inject(Router).navigate(["/login"])].
Les guards fonctions sont plus légers que les guards classes et sont la recommandation actuelle.

  • --hmr active le Hot Module Replacement qui remplace les modules modifiés sans recharger la page entière (bonne réponse)
  • --hmr est plus lent
  • --hmr désactive le rechargement automatique
  • Il n'y a pas de différence
Explication : Le HMR (Hot Module Replacement) permet de remplacer les modules modifiés en cours d'exécution sans recharger toute la page, préservant ainsi l'état de l'application (formulaires remplis, etc.). Cela accélère le développement.
Le HMR fonctionne particulièrement bien pour les changements de styles et de templates.

  • Fichier de configuration central pour les providers et features de l'application (bonne réponse)
  • Fichier de configuration du routeur uniquement
  • Fichier de configuration des variables d'environnement
  • Fichier de configuration TypeScript
Explication : Dans les applications standalone, app.config.ts exporte une fonction provideAppConfig() qui centralise les providers de l'application (routeur, HttpClient, etc.). Elle est utilisée dans main.ts avec provideRouter() et autres.
Exemple : export const appConfig: ApplicationConfig = { providers: [provideRouter(routes), provideHttpClient()] };

  • Importer provideHttpClient() dans app.config.ts, puis injecter HttpClient dans le composant (bonne réponse)
  • Importer HttpClientModule directement dans le composant
  • Utiliser fetch() à la place
  • HttpClient n'est pas disponible dans les composants standalone
Explication : Dans une application standalone, on configure provideHttpClient() dans app.config.ts (ou dans les providers du composant racine). Ensuite, on injecte HttpClient normalement dans n'importe quel composant ou service.
Pour les intercepteurs HTTP, utilisez provideHttpClient(withInterceptors([monInterceptor])).

  • Une fonction qui configure le routeur dans une application standalone
  • Une alternative à RouterModule.forRoot()
  • Un fournisseur de routes pour les composants
  • Toutes ces réponses sont correctes (bonne réponse)
Explication : provideRouter() est la façon moderne de configurer le routeur dans les applications standalone. Elle remplace RouterModule.forRoot() utilisée avec les NgModules. Elle prend un tableau de routes en paramètre.
Exemple : providers: [provideRouter(routes, withComponentInputBinding(), withViewTransitions())]

  • Importer provideAnimations() dans app.config.ts (bonne réponse)
  • Importer BrowserAnimationsModule dans le composant racine
  • Ajouter @angular/animations dans le package.json
  • Les animations ne sont plus supportées
Explication : Dans une application standalone, on utilise provideAnimations() (ou provideNoopAnimations() pour désactiver) dans le tableau des providers de app.config.ts. Cela remplace BrowserAnimationsModule.
N'oubliez pas d'importer provideAnimations depuis @angular/platform-browser/animations.

  • @Input({ required: true }) monInput: string; (bonne réponse)
  • @RequiredInput() monInput: string;
  • @Input() required monInput: string;
  • monInput: string = required();
Explication : Depuis Angular 16, on peut marquer un @Input() comme requis avec l'option required: true. Si le parent ne fournit pas cette entrée, Angular lèvera une erreur à la compilation ou à l'exécution.
Cette fonctionnalité aide à détecter les oublis d'entrées obligatoires et rend les composants plus robustes.

  • toObservable(monSignal) (bonne réponse)
  • monSignal.toObservable()
  • fromSignal(monSignal)
  • signalToObservable(monSignal)
Explication : La fonction toObservable() (importée de @angular/core/rxjs-interop) convertit un signal en Observable. L'Observable émet chaque fois que le signal change.
Utile pour intégrer des signaux avec des APIs qui attendent des Observables (comme certaines bibliothèques ou async pipe).

  • Une fonctionnalité qui permet des animations fluides entre les routes ou les états (bonne réponse)
  • Une nouvelle façon de créer des composants
  • Un outil de débogage visuel
  • Un remplacement du router-outlet
Explication : Angular 17+ intègre la View Transition API, une fonctionnalité du navigateur qui permet des animations fluides entre différents états de l'interface, notamment lors des changements de route. On l'active avec withViewTransitions() dans provideRouter().
Cela permet des animations natives très performantes sans avoir à configurer manuellement des transitions complexes.

  • Permet d'injecter des services dans des fonctions utilitaires, guards, ou resolvers (bonne réponse)
  • C'est plus rapide à l'exécution
  • C'est obligatoire dans les composants standalone
  • Il n'y a aucun avantage
Explication : La fonction inject() peut être utilisée dans n'importe quel contexte (fonctions, guards, resolvers, services), pas seulement dans les constructeurs de classes. Cela rend le code plus flexible et permet des patterns d'injection plus avancés.
Exemple : export const authGuard = () => { const auth = inject(AuthService); return auth.isLoggedIn(); }

  • Pour annuler les requêtes précédentes et ne garder que la dernière demande (bonne réponse)
  • Pour exécuter des requêtes en parallèle
  • Pour transformer un tableau en observable
  • Pour filtrer les valeurs nulles
Explication : switchMap souscrit à un nouvel Observable et annule automatiquement la souscription précédente. Idéal pour les recherches en temps réel : si l'utilisateur tape rapidement, les requêtes précédentes sont annulées et seule la dernière est conservée.
Pattern classique : this.searchTerm$.pipe(debounceTime(300), distinctUntilChanged(), switchMap(term => this.api.search(term))).subscribe().

  • mergeMap exécute les Observables en parallèle ; concatMap les exécute séquentiellement (bonne réponse)
  • mergeMap est plus rapide que concatMap
  • concatMap annule les Observables précédents ; mergeMap les conserve
  • Ils sont identiques
Explication : mergeMap souscrit immédiatement à chaque nouvel Observable, permettant l'exécution parallèle. concatMap attend que le premier Observable se complète avant de s'abonner au suivant (séquentiel, ordre garanti).
Pour des opérations où l'ordre est important (ex: sauvegardes successives), utilisez concatMap. Pour des opérations indépendantes (ex: suppression de plusieurs fichiers), utilisez mergeMap.

  • Utiliser catchError dans pipe() pour intercepter et gérer les erreurs
  • Utiliser try/catch directement
  • Utiliser l'option error du subscribe()
  • Les deux : catchError dans pipe() et le callback error de subscribe() (bonne réponse)
Explication : Deux approches existent : pipe(catchError(err => of(valeurDefaut))) pour transformer l'erreur en Observable, et le callback error dans subscribe({ next, error, complete }) pour réagir à l'erreur. La combinaison permet une gestion robuste.
Exemple : this.http.get("/api").pipe(catchError(err => { this.showError(err); return EMPTY; })).subscribe(data => ...).

  • Utiliser FormBuilder pour créer le FormGroup, définir les Validators, et vérifier form.valid avant soumission (bonne réponse)
  • Utiliser ngModel avec l'attribut required
  • Utiliser un service de validation externe
  • La validation est gérée automatiquement par Angular
Explication : Avec les reactive forms : fb.group({ email: ["", [Validators.required, Validators.email]], password: ["", [Validators.required, Validators.minLength(8)]] }). Dans le template, [formGroup]="form", formControlName="email", et vérifier form.valid avant soumission.
Pour des validateurs asynchrones (ex: vérifier si l'email existe déjà), utilisez le troisième paramètre de FormControl : new FormControl("", [Validators.required], [this.emailExistsValidator]).

  • Il retarde l'émission d'une valeur jusqu'à ce qu'un délai sans nouvelle valeur soit écoulé, utile pour les recherches en temps réel (bonne réponse)
  • Il retarde l'initialisation du composant
  • Il limite le nombre de requêtes HTTP par seconde
  • Il empêche la détection de changement pendant un délai
Explication : debounceTime(ms) attend que l'Observable soit "silencieux" pendant le délai spécifié avant d'émettre. Pour une recherche, si l'utilisateur tape rapidement, seule la valeur finale (après arrêt) est émise, évitant les requêtes inutiles.
Pattern classique : valueChanges.pipe(debounceTime(300), distinctUntilChanged(), switchMap(term => search(term))).

  • Utiliser @Directive({ selector: "[maDirective]" }) et modifier l'élément hôte via ElementRef ou HostBinding (bonne réponse)
  • Utiliser @Component sans template
  • Utiliser ng generate attribute
  • Utiliser @Pipe à la place
Explication : Une directive d'attribut se crée avec @Directive({ selector: "[maDirective]" }). Pour modifier l'hôte, injectez ElementRef ou utilisez @HostBinding("style.color") color = "red" et @HostListener("mouseenter").
Exemple de directive de surbrillance : @HostBinding("style.backgroundColor") bgColor = "transparent"; @HostListener("mouseenter") enter() { this.bgColor = "yellow"; }.

  • Utiliser trackBy avec une fonction qui retourne un identifiant unique
  • Utiliser OnPush sur le composant parent
  • Utiliser virtual scrolling avec CDK
  • Toutes ces réponses sont des optimisations valides (bonne réponse)
Explication : Les trois approches sont complémentaires : trackBy évite la recréation des éléments DOM qui n'ont pas changé ; OnPush évite de re-vérifier les composants enfants si leurs inputs n'ont pas changé ; le virtual scrolling (CDK) n'affiche que les éléments visibles pour les longues listes.
Pour les longues listes (>50 éléments), combinez trackBy + OnPush + ScrollingModule du CDK Angular pour des performances optimales.

  • Un Observable qui émet la dernière valeur stockée à chaque nouvelle souscription et peut être mis à jour avec .next() (bonne réponse)
  • Un service Angular qui gère l'état global
  • Un pipe qui met en cache les valeurs
  • Un type de requête HTTP
Explication : BehaviorSubject<T> est un Observable spécial qui stocke la dernière valeur. Les nouveaux souscripteurs reçoivent immédiatement la valeur actuelle. On l'utilise dans les services pour partager l'état : private state = new BehaviorSubject<User|null>(null); readonly state$ = this.state.asObservable(); update(u: User) { this.state.next(u); }.
Exposez toujours un Observable (via .asObservable()) publiquement et gardez le Subject privé pour éviter que les consommateurs ne puissent appeler .next().

  • Émet un tableau des dernières valeurs de chaque Observable quand n'importe lequel émet une nouvelle valeur (bonne réponse)
  • Combine plusieurs Observables en un seul de manière séquentielle
  • Attend que tous les Observables se complètent avant d'émettre
  • Prend la valeur du premier Observable qui émet
Explication : combineLatest([obs1$, obs2$, obs3$]) souscrit à tous les Observables et émet un tableau [val1, val2, val3] à chaque fois que l'un d'eux émet, en utilisant les dernières valeurs connues des autres. Il n'émet rien tant que chaque Observable n'a pas émis au moins une fois.
Idéal pour combiner des filtres : combineLatest([search$, category$, sortOrder$]).pipe(switchMap(([search, cat, sort]) => api.getList(search, cat, sort))).

  • effect() crée un side-effect qui s'exécute automatiquement quand les signals dont il dépend changent (bonne réponse)
  • effect() est un alias de ngOnChanges()
  • effect() remplace subscribe() pour les Observables
  • effect() est utilisé pour créer des animations
Explication : effect(() => { console.log(count()); }) crée un effet qui s'exécute automatiquement quand les signals lus à l'intérieur changent. Contrairement aux computed signals, les effects sont utilisés pour les side-effects (logging, appels HTTP, manipulation du DOM).
Les effects sont détruits automatiquement quand le composant est détruit. Évitez de modifier des signals dans un effect pour prévenir les boucles infinies.

  • loadChildren: () => import("./feature/feature.routes").then(r => r.FEATURE_ROUTES) (bonne réponse)
  • lazy: true dans la configuration de la route
  • loadComponent pour toutes les routes du module
  • asyncRoute: true dans angular.json
Explication : Pour lazy-loader un ensemble de routes, utilisez loadChildren avec l'importation dynamique d'un fichier de routes standalone : { path: "feature", loadChildren: () => import("./feature/feature.routes").then(r => r.FEATURE_ROUTES) }.
Différence avec loadComponent : loadComponent charge un seul composant standalone ; loadChildren charge un ensemble de routes (un fichier de routes ou un module).

  • Écrire une fonction HttpInterceptorFn et l'enregistrer avec withInterceptors() dans provideHttpClient() (bonne réponse)
  • Implémenter la classe HttpInterceptor et l'ajouter dans providers
  • Utiliser le décorateur @HttpInterceptor
  • Surcharger la méthode http.request()
Explication : Angular 17+ recommande les intercepteurs fonctionnels : export const authInterceptor: HttpInterceptorFn = (req, next) => { const token = inject(AuthService).getToken(); return next(req.clone({ setHeaders: { Authorization: token } })); }; puis dans app.config.ts : provideHttpClient(withInterceptors([authInterceptor])).
Les intercepteurs fonctionnels sont plus simples et plus légers que les intercepteurs de classe. Ils peuvent utiliser inject() pour accéder aux services.

  • Utiliser @angular/animations avec trigger(), state(), transition(), animate() (bonne réponse)
  • Utiliser des animations CSS uniquement
  • Utiliser la bibliothèque GSAP obligatoirement
  • Utiliser Web Animations API directement
Explication : Les animations Angular se définissent dans le décorateur @Component via animations: [trigger("nomAnimation", [state(), transition(), animate()])]. On lie l'animation dans le template avec [@nomAnimation]="etat".
N'oubliez pas d'activer les animations : provideAnimations() dans app.config.ts. Importez les fonctions d'animation depuis @angular/animations.

  • Angular génère des nonces pour les styles inline via le compilateur AOT, permettant une CSP stricte (bonne réponse)
  • Angular ne supporte pas le CSP
  • Il faut désactiver le CSP pour utiliser Angular
  • Le CSP est configuré dans angular.json
Explication : Angular peut fonctionner avec une CSP stricte. En activant la politique CSP-Nonce dans angular.json, le compilateur AOT génère des nonces uniques pour les styles inline, permettant d'interdire unsafe-inline dans la CSP.
Pour les tests, évitez les styles inline non nécessaires. Pour la production, configurez votre serveur web pour envoyer les en-têtes CSP appropriés.

  • Les smart components gèrent l'état et les données ; les dumb components reçoivent des @Input() et émettent des @Output() sans logique métier (bonne réponse)
  • Les smart components utilisent AI ; les dumb components n'ont pas de logique
  • Les smart components sont des services ; les dumb components sont des directives
  • Ce pattern n'existe pas dans Angular
Explication : Ce pattern architectural sépare les responsabilités : les "smart components" (containers) gèrent l'état, les appels de services, et les données. Les "dumb components" (presentational) reçoivent des données via @Input(), affichent et émettent des événements via @Output() sans accéder directement aux services.
Avantages : les dumb components sont plus faciles à tester, réutilisables et avec OnPush ils offrent de meilleures performances.

  • Lie automatiquement les paramètres de route aux @Input() du composant (bonne réponse)
  • Permet le data-binding entre composants frères
  • Active le two-way binding global
  • Permet d'injecter le router dans le composant
Explication : Avec provideRouter(routes, withComponentInputBinding()), Angular lie automatiquement les paramètres de route, query params et données de route aux propriétés @Input() du composant routé. Plus besoin d'injecter ActivatedRoute pour les cas simples.
Exemple : route { path: "users/:id" }@Input() id!: string; dans le composant. Angular injecte automatiquement la valeur du paramètre.

  • Injecter ChangeDetectorRef et appeler markForCheck() ou detectChanges() (bonne réponse)
  • Appeler this.update() dans le composant
  • Utiliser $apply() comme dans AngularJS
  • Déclencher un @Output() vide
Explication : Quand des données changent en dehors du cycle normal (setTimeout, WebSocket, callbacks), il faut notifier Angular manuellement. ChangeDetectorRef.markForCheck() marque le composant comme devant être vérifié au prochain cycle. detectChanges() force une détection immédiate.
Injectez avec private cdr = inject(ChangeDetectorRef) puis appelez this.cdr.markForCheck() après chaque mise à jour externe.

  • Un ensemble de primitives réutilisables pour créer des composants UI avancés (drag-and-drop, overlay, virtual scroll, a11y) (bonne réponse)
  • Un framework de tests pour Angular
  • Un outil de génération de code
  • Une bibliothèque pour les appels HTTP
Explication : Le CDK (Component Dev Kit) fournit des blocs de construction réutilisables : virtual scrolling pour les longues listes (ScrollingModule), drag-and-drop (DragDropModule), overlays, portals, accessibilité (a11y), et plus. Il est utilisé en interne par Angular Material.
Le CDK est indépendant du design Angular Material : vous pouvez l'utiliser même sans Material pour accéder à ses fonctionnalités avancées.

  • selected = output(); puis this.selected.emit(id); et (selected)="onSelect($event)" dans le template parent (bonne réponse)
  • selected = signal(0); puis selected.set(id);
  • @Output() selected = new SignalEmitter();
  • output(selected) dans le constructeur
Explication : La nouvelle API output() crée un OutputRef : protected deleted = output<number>();. Émission : this.deleted.emit(this.id). Le template parent reste identique : (deleted)="onDelete($event)". Plus lisible et cohérent avec les signals.
L'API output() peut aussi créer des outputs depuis des Observables : outputFromObservable(this.mySubject$).

  • Utiliser TestBed.configureTestingModule() pour configurer l'environnement de test, puis fixture.detectChanges() pour déclencher la détection (bonne réponse)
  • Instancier le composant avec new MonComposant()
  • Utiliser Jasmine directement sans TestBed
  • TestBed n'est pas nécessaire pour tester les composants standalone
Explication : Avec TestBed : TestBed.configureTestingModule({ imports: [MonComposantStandalone] }), puis const fixture = TestBed.createComponent(MonComposant). fixture.detectChanges() lance la détection initiale. fixture.nativeElement donne accès au DOM rendu.
Pour les composants standalone, importez-les directement dans imports au lieu de declarations.

  • Exécuter des side-effects sans modifier la valeur de l'Observable (logging, debug) (bonne réponse)
  • Transformer les valeurs d'un Observable
  • Filtrer les valeurs indésirables
  • Combiner plusieurs Observables
Explication : tap() permet d'effectuer des actions secondaires (side-effects) sans modifier le flux de données : obs$.pipe(tap(val => console.log("val:", val)), map(val => val * 2)). La valeur n'est pas modifiée par tap.
Utilisez tap pour le débogage ou pour déclencher des actions comme afficher un spinner : tap(() => this.loading = true).

  • Utiliser toSignal() pour convertir valueChanges ou statusChanges en signal (bonne réponse)
  • FormControl expose directement un signal
  • Les signals remplacent les FormControl
  • Aucune intégration n'est possible
Explication : On peut convertir les Observables de formulaires en signals : formValue = toSignal(this.form.valueChanges, { initialValue: this.form.value }). Cela permet d'utiliser la valeur du formulaire dans des computed signals ou des effects.
Angular travaille sur une intégration plus profonde des formulaires avec les signals. En attendant, toSignal(valueChanges) est le pont le plus pratique.

  • Utiliser plusieurs pour projeter du contenu dans des emplacements nommés (bonne réponse)
  • Afficher le même contenu plusieurs fois
  • Projeter du contenu dans plusieurs composants
  • Dupliquer automatiquement le contenu
Explication : La projection multi-slot permet de définir plusieurs points de projection dans un composant via des sélecteurs CSS : <ng-content select="[header]">, <ng-content select="[body]">. Le parent projette dans les slots : <span header>Titre</span>.
Le sélecteur peut être un attribut (select="[attr]"), une classe (select=".cls"), un tag (select="header") ou un composant (select="app-header").

  • Exécute plusieurs Observables en parallèle et émet un tableau des dernières valeurs quand tous sont complétés (bonne réponse)
  • Exécute des Observables séquentiellement
  • Annule tous les Observables si l'un échoue
  • Combine les Observables et émet dès que le premier se complète
Explication : forkJoin([obs1$, obs2$, obs3$]) lance tous les Observables en parallèle et émet un seul tableau [res1, res2, res3] quand TOUS sont complétés. Si l'un échoue, tout échoue. Idéal pour charger plusieurs ressources indépendantes simultanément.
Exemple : forkJoin({ user: http.get("/user"), settings: http.get("/settings") }).subscribe(({ user, settings }) => ...).

  • Utiliser resolve: { data: () => inject(Service).load() } dans la configuration de la route (bonne réponse)
  • Utiliser ngOnInit() pour charger les données avant le rendu
  • Utiliser un guard CanActivate pour pré-charger les données
  • Importer ResolverModule dans app.config.ts
Explication : Depuis Angular 14+, les resolvers peuvent être des fonctions : { path: "user/:id", component: UserComponent, resolve: { user: () => inject(UserService).getUser(inject(ActivatedRouteSnapshot).params["id"]) } }. Les données sont disponibles via ActivatedRoute.data.
Ou avec withComponentInputBinding(), le résolveur sera automatiquement injecté dans le @Input() correspondant : @Input() user!: User;.

  • Met en cache les émissions d'un Observable pour partager le résultat entre plusieurs souscripteurs sans re-exécuter la source (bonne réponse)
  • Rejoue l'Observable depuis le début à chaque souscription
  • Partage la souscription entre les composants parents et enfants
  • Empêche les doublons dans un Observable
Explication : shareReplay(1) rend le dernier résultat disponible immédiatement aux nouveaux souscripteurs, sans re-exécuter la requête source. Idéal pour les appels HTTP partagés entre plusieurs composants.
Pattern courant dans les services : private config$ = this.http.get("/config").pipe(shareReplay(1)); — chaque composant qui souscrit obtient les données du cache, pas une nouvelle requête.

  • Utiliser un validateur au niveau du FormGroup qui compare les deux champs (bonne réponse)
  • Comparer dans le HTML avec une directive ngIf
  • Utiliser Validators.match()
  • La validation de groupe n'est pas possible
Explication : Un validateur de groupe accède à tous les champs : fb.group({ password: [""], confirm: [""] }, { validators: (g) => g.get("password")?.value === g.get("confirm")?.value ? null : { mismatch: true } }). L'erreur est sur le FormGroup, pas sur le FormControl.
Pour afficher l'erreur dans le template : *ngIf="form.errors?.["mismatch"] && form.get("confirm")?.touched".

  • Utiliser l'attribut i18n sur les éléments, puis ng extract-i18n et ng build --localize
  • Utiliser le pipe | translate avec ngx-translate
  • Utiliser des fichiers JSON de traduction manuellement
  • Toutes ces approches sont valides (bonne réponse)
Explication : Angular offre une solution i18n native (marqueurs i18n + build AOT) performante car les traductions sont compilées. Pour plus de flexibilité, des bibliothèques comme ngx-translate permettent des changements de langue à chaud sans rebuild.
Pour les petits projets ou les changements de langue en temps réel, préférez ngx-translate. Pour les grandes applications à performances critiques, préférez l'i18n natif d'Angular.

  • Utiliser des attributs ARIA, les rôles sémantiques, les directives CDK a11y, et gérer le focus manuellement (bonne réponse)
  • Ajouter un attribut accessible="true" sur chaque élément
  • Utiliser le service AccessibilityService d'Angular
  • L'accessibilité est gérée automatiquement par Angular
Explication : L'accessibilité nécessite : HTML sémantique, attributs ARIA (aria-label, aria-hidden, role), gestion du focus (@angular/cdk/a11y avec FocusTrap, LiveAnnouncer), et navigation clavier. Angular ne gère pas automatiquement l'a11y.
Utilisez @angular/cdk/a11y pour les composants interactifs : FocusMonitor, FocusTrap, et LiveAnnouncer pour les annonces aux lecteurs d'écran.

  • @defer (when condition) { ... } pour un déclenchement basé sur une expression booléenne (bonne réponse)
  • @defer [condition] { ... }
  • @lazy (condition) { ... }
  • defer="condition" sur l'élément HTML
Explication : Le déclencheur when permet de charger le contenu défèré quand une expression devient true : @defer (when isLoggedIn) { <profil-section /> } @placeholder { <skeleton /> }. Il surveille l'expression à chaque cycle de détection.
Les blocs @defer supportent plusieurs déclencheurs : @defer (on viewport; when hasPermission) — charge quand l'élément est visible ET la condition est vraie.