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

AngularJS — Entretien Senior

Angularjs Senior Quiz-Angularjs-Senior Digest-Cycle Compile Directives-Avancees Migration-Angular Performances-Angularjs Tests-Unitaires Entretien-Senior-Angularjs Qcm-Angularjs-Avance Legacy-Frontend Javascript Karma-Jasmine
Sénior 🔀 Mixte 20 questions ⏱ 15 min
📝

AngularJS — Entretien Senior

20 questions avancées AngularJS : cycle $digest, $compile, directives avancées, tests unitaires, migration Angular, performances. Idéal pour réussir un entretien AngularJS Senior.

20 questions ⏱ ~15 min Niveau Sénior

Partager

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

Voici l'intégralité des 60 questions de « AngularJS — Entretien Senior », 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.

  • Le $digest parcourt l'arbre DOM pour détecter les changements visuels
  • Le $digest exécute tous les watchers en boucle jusqu'à stabilité — chaque watcher supplémentaire augmente le coût O(n) (bonne réponse)
  • Le $digest ne peut pas être optimisé car il est fondamental
  • Le $digest ne s'exécute que si $apply() est appelé manuellement
Explication : Le cycle $digest parcourt tous les watchers ($$watchers) en comparant les valeurs actuelles aux anciennes (dirty-checking). Il relance jusqu'à stabilité (max 10 boucles). Avec n watchers, chaque $digest coûte O(n). Une UI fluide à 60fps laisse ~16ms par frame — avec 5000 watchers complexes, cela peut dépasser ce budget.
Outils de diagnostic : angular-performance devtools, $rootScope.$$watchersCount. Stratégies : one-time binding (::), track by, ng-if vs ng-show, débouchement des $watch.

  • Angular continue indéfiniment jusqu'à stabilité
  • Angular lève une exception "10 $digest() iterations reached. Aborting!" (bonne réponse)
  • Angular s'arrête silencieusement sans mettre à jour le DOM
  • Angular passe en mode dégradé avec mise à jour partielle
Explication : AngularJS lève l'erreur $rootScope:infdig : "10 $digest() iterations reached. Aborting!" quand la limite d'itérations est dépassée. Cela indique généralement une boucle infinie : un watcher modifie une valeur qui déclenche un autre watcher, etc. La limite de 10 est configurable via $rootScopeProvider.digestTtl().

  • Un service pour valider le HTML
  • Le service qui compile des templates HTML en fonctions de liaison utilisables dans le code JavaScript (bonne réponse)
  • Un outil de minification du code AngularJS
  • Un service de débogage des directives
Explication : $compile(htmlString)(scope) compile dynamiquement du HTML AngularJS et le lie à un scope. Utilisé pour créer des directives qui génèrent du HTML dynamiquement, pour les widgets modaux, les tooltips, ou toute situation où on doit compiler du HTML au runtime. Retourne une fonction de liaison (link function).
Pattern : var el = $compile("")($scope); $element.append(el); — à utiliser avec parcimonie car contourne le mécanisme déclaratif standard.

  • En surchargeant le service avec le même nom
  • En utilisant app.decorator("nomService", function($delegate) { return decoratedService; }) (bonne réponse)
  • En étendant le prototype du service
  • En créant un service avec le suffixe "Decorator"
Explication : La décoration via $provide.decorator() (ou app.decorator()) permet d'étendre ou modifier des services existants sans les remplacer. $delegate est l'instance originale du service. On peut ajouter des méthodes, logguer les appels, ou modifier le comportement.
Pattern courant : décorer $http pour ajouter du logging, ou décorer $exceptionHandler pour centraliser les erreurs.

  • En réécrivant entièrement l'application en une seule fois
  • En utilisant UpgradeModule pour faire coexister AngularJS et Angular dans la même application (bonne réponse)
  • En utilisant un proxy serveur pour router les URLs vers l'une ou l'autre version
  • ngUpgrade n'est plus supporté depuis Angular 10
Explication : UpgradeModule (@angular/upgrade) permet de faire coexister les deux frameworks. Stratégie "strangler fig" : 1) Bootstrapper Angular avec UpgradeModule. 2) Migrer composant par composant. 3) Utiliser downgradeComponent() pour utiliser un composant Angular dans AngularJS, et UpgradeComponent pour l'inverse.
Ordre recommandé : migrer d'abord les services (plus faciles), puis les composants feuilles, puis remonter vers la racine.

  • C'est un hash de sécurité pour protéger les données
  • C'est un identifiant interne ajouté par ng-repeat pour suivre les objets — peut causer des problèmes de comparaison JSON (bonne réponse)
  • C'est un cache de performance pour les éléments DOM
  • C'est la clé de chiffrement utilisée par $sce
Explication : AngularJS ajoute $$hashKey aux objets trackés par ng-repeat pour les identifier. Cela pollue les objets JavaScript et peut causer des problèmes lors de la sérialisation JSON (la propriété apparaît). angular.toJson() les nettoie. Solution : utiliser track by item.id pour éviter l'ajout de $$hashKey.

  • En utilisant window.fetch pour mocker les requêtes
  • En utilisant $httpBackend.expectGET(url).respond(data) dans beforeEach et .flush() après l'action (bonne réponse)
  • En remplaçant $http par un faux service injecté
  • Les requêtes HTTP ne peuvent pas être testées unitairement
Explication : Avec ngMock : $httpBackend.expectGET("/api/data").respond(200, mockData) définit l'attente. Après l'action testée, $httpBackend.flush() simule la réponse HTTP et déclenche les callbacks. $httpBackend.verifyNoOutstandingExpectation() vérifie que toutes les attentes ont été satisfaites.
Pattern complet : beforeEach(inject(function(_$httpBackend_) { $httpBackend = _$httpBackend_; })); afterEach(function() { $httpBackend.verifyNoOutstandingExpectation(); $httpBackend.verifyNoOutstandingRequest(); });

  • Ils sont identiques — les deux vérifient que la requête a été faite
  • when() définit une réponse sans vérifier si la requête est faite ; expect() vérifie que la requête est effectuée (bonne réponse)
  • when() est pour les tests asynchrones, expect() pour les synchrones
  • expect() supporte les regex, when() non
Explication : when() définit une réponse stub sans assertion sur son appel. expect() vérifie obligatoirement que la requête est faite (échec de test si elle ne l'est pas). Utilisez when() pour les requêtes d'initialisation non critiques, expect() pour les comportements à tester.

  • En ajoutant terminal: true à la directive
  • En utilisant compile() pour transformer le template une fois avant clonage, évitant les opérations répétitives (bonne réponse)
  • En désactivant le $watch dans la fonction link
  • En utilisant $scope.$evalAsync pour différer la compilation
Explication : Dans compile(tElement, tAttrs), on peut modifier le template DOM avant qu'il soit cloné pour chaque item ng-repeat. Les modifications coûteuses sont ainsi faites une seule fois au lieu de n fois. La fonction compile retourne la fonction link (ou un objet {pre, post}), qui s'exécute pour chaque instance.
Typiquement utilisé pour : précompiler des expressions complexes, retirer des nœuds conditionnels connus à la compilation, restructurer le template.

  • La version d'AngularJS en cours d'exécution
  • L'indicateur de phase en cours du cycle digest — permet d'éviter un double $apply (bonne réponse)
  • Le compteur d'itérations du cycle $digest
  • La liste des watchers actifs
Explication : $rootScope.$$phase indique si un cycle digest est en cours ("$apply" ou "$digest") ou null. Appeler $apply() pendant un cycle provoque une erreur. Pattern sécurisé : if (!$rootScope.$$phase) { $scope.$apply(fn); } else { fn(); }. En pratique, utilisez plutôt $scope.$evalAsync(fn).
$evalAsync est préféré à $$phase car il est un API public et gère correctement le timing.

  • Ils sont identiques et interchangeables
  • $applyAsync regroupe les appels $apply en une seule exécution asynchrone ; $evalAsync diffère l'évaluation dans le cycle digest courant ou prochain (bonne réponse)
  • $evalAsync est plus récent et remplace $applyAsync
  • $applyAsync est synchrone, $evalAsync asynchrone
Explication : $evalAsync(fn) exécute fn dans le cycle digest courant ou le prochain — utile pour différer du travail tout en restant dans le flux digest. $applyAsync(fn) regroupe plusieurs appels $apply proches dans le temps en une seule exécution (debounce) — optimise les réponses HTTP multiples simultanées.
$http utilise $applyAsync par défaut depuis AngularJS 1.3. Configurable via $httpProvider.useApplyAsync(true/false).

  • En utilisant transclude: true et plusieurs ng-transclude
  • En utilisant transclude: { slot1: "?tag1", slot2: "?tag2" } et ng-transclude="slot1" (bonne réponse)
  • Multi-slot n'est pas supporté par AngularJS
  • En utilisant ng-transclude avec un index numérique
Explication : AngularJS 1.5+ supporte la transclusion multi-slot : transclude: { titleSlot: "?h1", contentSlot: "p" }. Dans le template : <div ng-transclude="titleSlot">. Dans l'utilisation : <ma-directive><h1>Titre</h1><p>Contenu</p></ma-directive>.

  • Un service pour créer des animations CSS3 depuis JavaScript
  • Le service qui détecte et orchestre les animations pendant les transitions de directives structurelles (bonne réponse)
  • Un polyfill pour Web Animations API
  • Un service uniquement pour les animations d'entrée/sortie
Explication : $animate est l'API JavaScript de ngAnimate. Il expose des méthodes comme enter(), leave(), move(), addClass(), removeClass() qui déclenchent des animations CSS définies par convention (.ng-enter, .ng-leave). Les directives structurelles l'utilisent internement.
Utilisez $animate directement quand vous créez des directives qui nécessitent des animations (modales, tooltips). L'API gère automatiquement le timing et les callbacks de fin d'animation.

  • app.provider("MonService", function() { this.$get = function() {} })
  • app.provider("MonService", { config: function() {}, $get: function() {} })
  • La première syntaxe, avec des propriétés de configuration et une méthode $get (bonne réponse)
  • app.factory("MonProvider", function($provide) {})
Explication : Un provider expose des méthodes de configuration publiques ET une méthode $get. Exemple : app.provider("Config", function() { var apiUrl = "/api"; this.setApiUrl = function(url) { apiUrl = url; }; this.$get = function() { return { url: apiUrl }; }; }). Configurable dans .config(["ConfigProvider", function(cp) { cp.setApiUrl("/v2"); }]).

  • Il importe des modules JavaScript externes
  • Il demande le controller d'une directive ancêtre ou sœur, injecté dans la fonction link (bonne réponse)
  • Il valide que les attributs requis sont présents
  • Il charge des templates HTML externes asynchronement
Explication : La propriété require dans une directive permet d'accéder au controller d'une directive parente. Exemples : require: "^ngModel" (directive ancêtre), require: "?^form" (optionnel), require: ["ngModel", "^form"] (plusieurs). Le controller est passé comme 4ème argument à la fonction link.
Pattern courant : valider un champ (accès à ngModelController) ou créer des composants composés (tabs/tab, accordion/panel).

  • Exige le controller ngForm sur l'élément même
  • Exige le controller ngForm sur un ancêtre strict (pas l'élément même) (bonne réponse)
  • Exige optionnellement le controller ngForm sur un ancêtre
  • Exige le controller ngForm dans le scope courant
Explication : Le préfixe ^^ cherche la directive sur les ancêtres en excluant l'élément courant. ^ inclut l'élément courant. ?^ rend la recherche optionnelle (retourne null si non trouvé plutôt que d'erreur). La combinaison ?^^ = optionnel + ancêtres stricts.

  • En utilisant ng-virtual-repeat avec angularjs-core
  • En utilisant des bibliothèques comme md-virtual-repeat (Angular Material) ou ng-virtual-scroll pour ne rendre que les éléments visibles (bonne réponse)
  • En limitant ng-repeat à 50 éléments maximum
  • En utilisant CSS overflow:hidden sur le conteneur
Explication : Le virtual scrolling ne rend que les éléments visibles dans le viewport + un buffer. Angular Material fournit md-virtual-repeat. Des bibliothèques tierces comme angular-vs-repeat ou ng-infinite-scroll (infinite scroll vs virtual) sont aussi populaires. Réduit le DOM de milliers d'éléments à quelques dizaines.
Virtual scroll = seules les lignes visibles sont dans le DOM. Infinite scroll = toutes les lignes chargées s'accumulent. Pour 10 000+ éléments, le virtual scroll est indispensable.

  • Elle arrête AngularJS quand une erreur survient dans la directive
  • Elle empêche la compilation des directives de priorité inférieure sur le même élément (bonne réponse)
  • Elle indique que la directive est la dernière à être utilisée dans l'application
  • Elle désactive la transclusion
Explication : terminal: true arrête le processus de compilation pour les directives de priorité inférieure sur le même élément. Utilisé par ng-if, ng-repeat etc. pour contrôler la compilation conditionnelle. Couplé avec une priorité élevée, permet d'intercepter la compilation avant les autres directives.

  • En ajoutant la directive dans le DOM réel et en lisant le résultat
  • En utilisant $compile(html)($scope) et en appelant $scope.$digest() pour forcer le rendu (bonne réponse)
  • Les directives ne peuvent pas être testées unitairement
  • En utilisant angular.test.directive()
Explication : Pattern de test de directive : var element = $compile("<mon-dir attr='val'></mon-dir>")($rootScope); $rootScope.$digest(); expect(element.text()).toBe("..."); expect(element.find("button").length).toBe(1);. L'élément compilé est un vrai élément DOM jQuery/jqLite.
Après compilation, isolez le scope avec element.isolateScope() si la directive a un scope isolé. Cela permet de tester les propriétés du scope isolé directement.

  • En surchargeant le service directement
  • En utilisant $provide.value() ou $provide.factory() dans beforeEach pour remplacer le service (bonne réponse)
  • Les services ne peuvent pas être mockés
  • En utilisant angular.mock.service()
Explication : Pour mocker un service dans un test : beforeEach(module("monApp", function($provide) { $provide.value("MonService", { getData: jasmine.createSpy().and.returnValue($q.when(mockData)) }); }));. Le mock remplace l'implémentation réelle pour toute la durée du test.

  • Un mécanisme pour exécuter les promises en parallèle
  • Ce n'est pas une API AngularJS — $q.race() n'existe pas nativement en AngularJS 1.x (bonne réponse)
  • Une fonction qui retourne la promise la plus rapide
  • Un outil de debugging pour les promises
Explication : AngularJS 1.x $q ne fournit pas race() nativement (contrairement à Promise ES6). Il faut l'implémenter manuellement ou utiliser une bibliothèque. AngularJS 1.7.3+ a ajouté un support limité. La workaround standard : $q.race = function(promises) { var d = $q.defer(); promises.forEach(p => $q.when(p).then(d.resolve, d.reject)); return d.promise; }.
Connaître les limites de $q vs Promise native montre une connaissance approfondie d'AngularJS.

  • Pour désactiver les logs de compilation dans la console
  • Pour supprimer les attributs de debug (ng-scope, ng-binding) du DOM en production, améliorant les performances (bonne réponse)
  • Pour désactiver les messages d'erreur AngularJS
  • Pour accélérer la phase de compilation initiale
Explication : En production, $compileProvider.debugInfoEnabled(false) dans .config() supprime les attributs CSS/data injectés par Angular pour le debugging (ng-scope, ng-binding, ng-isolate-scope). Cela réduit la taille du DOM et améliore les performances de ~5-15%. Les devtools Angular ne fonctionnent plus, mais c'est prévu en prod.

  • AngularJS gère automatiquement les dépendances circulaires
  • En utilisant $injector.get() à l'intérieur du service pour un chargement lazy
  • En créant un service intermédiaire qui rompt le cycle
  • Les deux dernières options sont des approches valides (bonne réponse)
Explication : Si ServiceA dépend de ServiceB et vice-versa, AngularJS lève une erreur. Solutions : 1) $injector.get("ServiceB") dans la méthode qui en a besoin (pas dans le constructeur) — lazy loading qui évite le cycle au bootstrap. 2) Service médiateur — ServiceC que A et B utilisent tous les deux. 3) Refactoriser pour supprimer la dépendance circulaire (meilleure approche).
Les dépendances circulaires indiquent généralement un problème de conception. Analysez si les responsabilités sont bien séparées.

  • Il compresse les templates pour réduire la taille
  • Il stocke les templates en mémoire pour éviter les requêtes HTTP répétées (bonne réponse)
  • Il valide la syntaxe des templates
  • Il précompile les templates en JavaScript
Explication : $templateCache est un service de cache pour les templates HTML. AngularJS y met automatiquement les templates chargés via ng-include ou les routes. On peut préremplir le cache lors du build (grunt-html2js, ng-html2js) pour éviter les requêtes HTTP en production.
En production, utilisez ng-html2js ou ngAnnotate pour intégrer tous les templates dans un bundle JavaScript — zéro requête HTTP pour les templates.

  • AngularJS n'offre aucune protection XSS par défaut
  • Via $sce (Strict Contextual Escaping) qui sanitize les expressions et HTML selon leur contexte d'utilisation (bonne réponse)
  • Via la validation de toutes les URLs avec une liste blanche stricte
  • Via le chiffrement de toutes les données du scope
Explication : $sce (Strict Contextual Escaping) est le mécanisme de sécurité central. Chaque contexte (HTML, CSS, URL, JS, resourceURL) a ses propres règles. ng-bind-html requiert un HTML sanitizé. Les URLs dans ng-src/ng-href sont validées. Le service $sanitize (ngSanitize) whitelist le HTML autorisé.
Ne jamais utiliser $sce.trustAs*() pour des données utilisateur. C'est uniquement pour du contenu dont VOUS contrôlez la source.

  • Un service pour parser du JSON
  • Un service qui compile une expression AngularJS en une fonction getter/setter réutilisable (bonne réponse)
  • Un service pour valider les templates HTML
  • Un polyfill pour JSON.parse
Explication : $parse(expression) retourne une fonction fn(scope) qui évalue l'expression dans un contexte. La fonction a aussi une méthode assign(scope, value) pour setter la valeur. Plus performant que d'évaluer l'expression dans le template à chaque cycle, utile dans les directives à haute fréquence.

  • AngularJS gère automatiquement toute la mémoire, aucune action nécessaire
  • En dé-enregistrant les watchers, event listeners et timers dans l'événement $destroy du scope (bonne réponse)
  • En utilisant ng-if au lieu de ng-show pour tous les éléments
  • En appelant $scope.$destroy() manuellement dans chaque contrôleur
Explication : Sources courantes de fuites : 1) Watchers non supprimés (conserver la fonction retournée par $watch et l'appeler dans $destroy). 2) Event listeners globaux (window.addEventListener — supprimer dans $destroy). 3) $interval/$timeout non annulés. Pattern : $scope.$on("$destroy", function() { cleanup(); }).
Outil de détection : Chrome DevTools Memory Heap Snapshot. Comparez avant/après navigation pour identifier les objets retenus. Les contrôleurs qui restent en mémoire après navigation sont un signe de fuite.

  • AngularJS est incompatible avec CSP
  • En activant $compileProvider.aHrefSanitizationWhitelist() et en évitant ng-csp ou en activant ng-csp pour désactiver les eval (bonne réponse)
  • En ajoutant ng-csp="unsafe-eval" dans le HTML
  • CSP est géré automatiquement par AngularJS
Explication : CSP peut bloquer eval() utilisé par AngularJS pour certaines expressions. Solution : 1) Ajouter ng-csp sur l'élément ng-app désactive l'usage d'eval (performances réduites). 2) Configurer $compileProvider.aHrefSanitizationWhitelist() pour les URLs. 3) Utiliser nonces CSP pour les scripts inline.

  • L'utilisation de Web Components standards avec AngularJS
  • Le pattern .component() qui simplifie les directives et prépare la migration Angular 2+ (bonne réponse)
  • La découpe de l'application en micro-frontends
  • L'utilisation exclusive de bibliothèques de composants UI
Explication : AngularJS 1.5 introduit .component() comme sucre syntaxique sur les directives. Les composants ont : scope isolé par défaut, controllerAs "vm" par défaut, bindings unidirectionnels (<), lifecycle hooks ($onInit, $onChanges, $onDestroy, $postLink). C'est la passerelle conceptuelle vers Angular 2+.
Migrer vers .component() est la première étape vers Angular 2+. Les lifecycle hooks et les bindings one-way sont identiques à Angular.

  • created, mounted, destroyed
  • $onInit, $onChanges, $onDestroy, $postLink, $doCheck (bonne réponse)
  • ngOnInit, ngOnDestroy, ngAfterViewInit
  • constructor, init, destroy
Explication : AngularJS 1.5+ composants exposent : $onInit() — après bindings initialisés, $onChanges(changesObj) — quand les bindings one-way changent, $onDestroy() — avant destruction, $postLink() — après compilation enfants (équivalent ngAfterViewInit Angular), $doCheck() — chaque cycle digest (utiliser avec soin).

  • Ce pattern n'est pas possible avec AngularJS
  • En utilisant un store service singleton, des actions, et en passant les données via bindings one-way (" (bonne réponse)
  • En désactivant le two-way binding globalement
  • En utilisant exclusivement $rootScope pour toutes les données
Explication : Pattern FLUX en AngularJS 1.5+ : 1) Store : service singleton avec l'état et les méthodes de mutation. 2) Actions : méthodes du store (addItem, removeItem). 3) Composants : reçoivent les données via bindings < (one-way) et appellent les actions via &. 4) Le store diffuse les changements via $broadcast ou callbacks.
ngRedux porte le pattern Redux sur AngularJS. Pour les grandes applications, c'est une architecture plus prévisible que le two-way binding libre.

  • Ils sont identiques
  • constant() est injectable dans .config(), value() ne l'est pas ; constant() ne peut pas être décoré (bonne réponse)
  • value() est injectable dans .config(), constant() non
  • constant() supporte les fonctions, value() non
Explication : constant() crée une valeur injectable dans la phase .config() ET dans les services/contrôleurs. Elle ne peut pas être décorée ($provide.decorator ne fonctionne pas sur les constants). value() n'est injectable que dans la phase run (services, contrôleurs) et peut être décorée.

  • Pour dégrader les performances d'un composant Angular en AngularJS
  • Pour utiliser un composant Angular 2+ dans une application AngularJS pendant la migration (bonne réponse)
  • Pour convertir un composant en directive
  • Pour rendre un composant compatible avec les anciens navigateurs
Explication : downgradeComponent({component: MonComposantAngular}) crée une directive AngularJS qui wrape un composant Angular 2+. Utilisé pendant la migration hybride avec UpgradeModule. Permet d'utiliser de nouveaux composants Angular dans les templates AngularJS existants sans réécrire toute la vue.
Stratégie : créer les nouveaux composants dans Angular, les exposer avec downgradeComponent() dans AngularJS, migrer progressivement les anciens composants.

  • Avec window.performance.measure()
  • Avec les DevTools Chrome Timeline et des extensions comme Batarang ou ng-inspector (bonne réponse)
  • Avec $rootScope.$$watchersTimer
  • Avec console.time() dans chaque watcher
Explication : Outils : 1) Batarang (extension Chrome) affiche le temps passé dans $digest et le nombre de watchers. 2) ng-inspector inspecte le scope. 3) Chrome DevTools Timeline enregistre les frames et identifie les longs JS tasks. 4) $rootScope.$$watchers liste tous les watchers actifs (debug).

  • Pour changer la langue du framework
  • Pour changer les délimiteurs d'interpolation {{ }} et éviter les conflits avec d'autres templates (Twig, Jinja) (bonne réponse)
  • Pour améliorer les performances d'interpolation
  • Pour activer l'interpolation dans les attributs HTML
Explication : Dans .config(), $interpolateProvider.startSymbol("{{") et endSymbol("}}") permettent de changer les délimiteurs d'interpolation. Exemple : les utiliser avec Twig (PHP) qui utilise aussi {{ }} — configurer AngularJS pour utiliser [[/]] à la place évite le conflit.
Cas d'usage réel : SPA Angular intégrée dans un CMS PHP/Twig ou un projet Django. Changer les délimiteurs est la solution propre.

  • En utilisant ng-language dans le HTML
  • En utilisant le module angular-translate ou ngI18n avec des tables de traduction (bonne réponse)
  • AngularJS ne supporte pas l'i18n
  • En créant un filtre "translate" personnalisé
Explication : La bibliothèque la plus populaire est angular-translate ($translate service, translate filter, translate directive). Elle supporte : chargement asynchrone des traductions, pluralisation, formatage, fallback. AngularJS 1.x fournit aussi une localisation basique (filtres number/date/currency) via les fichiers i18n du package.

  • AngularJS supporte nativement le SSR comme Angular Universal
  • Via des approches comme Prerender.io ou des solutions de rendu dynamique côté serveur (bonne réponse)
  • Le SSR n'est pas possible avec AngularJS
  • En utilisant Node.js natif avec le module angular-node
Explication : AngularJS n'a pas de support SSR natif (pas d'AngularJS Universal). Approches possibles : 1) Prerender.io (service tiers) rend la page avec un headless browser et sert le HTML pré-rendu aux crawlers. 2) PhantomJS/Rendertron en proxy. 3) isomorphic-angular (limité). C'est une limitation majeure d'AngularJS vs Angular.
C'est l'une des raisons clés pour migrer vers Angular 2+ (Angular Universal). Mentionnez cette limitation en entretien montre une connaissance des compromis.

  • Pour exécuter du code de manière asynchrone dans un setTimeout
  • Pour différer l'exécution d'une expression dans le cycle digest courant ou prochain, sans déclencher de $apply supplémentaire (bonne réponse)
  • Pour évaluer une expression dans un scope enfant
  • Pour annuler une évaluation en cours
Explication : $scope.$evalAsync(fn) diffère l'exécution de fn dans le cycle digest courant si possible, ou planifie un nouveau cycle. Contrairement à $timeout(fn, 0), il tente d'exécuter dans le cycle courant — plus performant et sans déclencher un nouveau cycle inutile.

  • Il n'y a pas de différence — les deux utilisent Jasmine
  • Les tests unitaires (Karma + Jasmine + ngMock) testent des unités isolées ; les tests E2E (Protractor) testent l'application complète dans un navigateur (bonne réponse)
  • Les tests E2E sont plus rapides que les tests unitaires
  • Protractor est remplacé par Karma depuis AngularJS 1.6
Explication : Tests unitaires : Karma (runner) + Jasmine (assertions) + ngMock (helpers Angular). Rapides, isolés, mockent les dépendances. Tests E2E : Protractor (wrapper Selenium/WebDriver) teste l'application réelle dans le navigateur. Lents mais testent les flux utilisateur complets. Complémentaires, pas interchangeables.
Note : Protractor est déprécié depuis Angular 12+. Pour AngularJS, il reste l'outil standard. Des alternatives modernes : Playwright, Cypress.

  • En créant un fichier test.config.js dans la racine
  • En créant karma.conf.js avec les frameworks, fichiers à charger et browsers (bonne réponse)
  • En ajoutant ng-test dans l'application
  • Karma se configure uniquement via CLI
Explication : karma.conf.js définit : frameworks: ["jasmine"], files: ["node_modules/angular/angular.js", "node_modules/angular-mocks/angular-mocks.js", "src/**/*.js", "test/**/*.spec.js"], browsers: ["ChromeHeadless"]. Le module angular-mocks est essentiel pour inject() et $httpBackend.

  • Le gestionnaire de modules AngularJS
  • Le service qui résout et fournit les dépendances — utile pour le chargement lazy ou briser les dépendances circulaires (bonne réponse)
  • Le service de configuration globale
  • $injector est uniquement pour le debugging
Explication : $injector est le mécanisme d'injection sous-jacent. $injector.get("NomService") retourne l'instance d'un service sans qu'il soit un paramètre de constructeur. Utile pour : briser des dépendances circulaires (appeler get() dans une méthode, pas le constructeur), créer des plugins ou extensions dynamiques.

  • En utilisant ng-switch pour chaque onglet
  • En utilisant require pour que les directives "tab" accèdent au controller de la directive "tabs" parent (bonne réponse)
  • En passant un index via ng-model à chaque tab
  • En utilisant $broadcast entre les composants tab et tabs
Explication : Pattern classique de directive composée : tabs expose un controller avec des méthodes addTab(), selectTab(). La directive tab utilise require: "^tabs" pour accéder à ce controller dans sa fonction link et s'enregistre. C'est le pattern "directive communication" standard.
Ce pattern est la base de nombreux composants UI complexes : accordéon, stepper, wizard. Maîtriser require + controller communication est essentiel.

  • En utilisant ng-error-route
  • En écoutant $routeChangeError sur $rootScope et en redirigeant (bonne réponse)
  • Les erreurs de resolve bloquent définitivement la navigation
  • En utilisant .catch() dans chaque résolution
Explication : Quand un resolve rejette sa promise, ngRoute émet $routeChangeError sur $rootScope. Dans .run(), écouter : $rootScope.$on("$routeChangeError", function(event, current, previous, rejection) { $location.path("/error"); });. Permet une gestion globale des erreurs de navigation.
Pattern complet : le resolve vérifie les droits, rejette si non autorisé, $routeChangeError redirige vers /login ou /403.

  • AngularJS ne fait aucun encodage par défaut
  • L'interpolation {{ }} encode automatiquement le HTML — ng-bind-html nécessite $sanitize ou $sce.trustAsHtml (bonne réponse)
  • L'encodage doit être fait manuellement par le développeur
  • AngularJS utilise DOMPurify automatiquement
Explication : L'interpolation {{ expression }} et ng-bind encodent automatiquement le HTML (escape des caractères spéciaux). Pour afficher du HTML brut, ng-bind-html requiert soit $sanitize (ngSanitize) qui whitelist le HTML, soit $sce.trustAsHtml() qui désactive la protection — à n'utiliser que pour du contenu de confiance.

  • Réécriture complète en une seule fois (big bang rewrite)
  • Migration incrémentale avec UpgradeModule : migrer services → composants feuilles → remonter vers la racine (bonne réponse)
  • Garder AngularJS indéfiniment — la migration n'est jamais nécessaire
  • Migrer uniquement les parties visibles aux utilisateurs
Explication : La stratégie recommandée par Google est la migration incrémentale ("strangler fig pattern") : 1) Préparer : utiliser .component(), controllerAs, TypeScript si possible. 2) Ajouter Angular avec UpgradeModule. 3) Migrer les services en premiers (injectables partout). 4) Migrer les composants feuilles avec downgradeComponent. 5) Remonter progressivement vers la racine.
Le big bang rewrite est risqué — nouveau code non testé, features manquantes, timeline imprévisible. L'incrémental permet de livrer de la valeur tout en migrant.

  • En utilisant $stateProvider.lazy()
  • En utilisant ocLazyLoad avec $stateProvider dans le resolve pour charger les modules à la demande (bonne réponse)
  • ui-router ne supporte pas le lazy loading
  • En utilisant ng-lazy sur les states
Explication : Pattern avec ocLazyLoad : $stateProvider.state("admin", { resolve: { load: function($ocLazyLoad) { return $ocLazyLoad.load(["admin.module.js"]); } }, template: "..." }). Le module est chargé et enregistré dans Angular avant l'activation de la route.

  • En augmentant la limite du cycle $digest
  • En différant le bootstrap (angular.bootstrap manuel), en préchargeant les templates et en minimisant les services lourds (bonne réponse)
  • En désactivant les directives non essentielles au démarrage
  • Le bootstrap ne peut pas être optimisé en AngularJS
Explication : Optimisations bootstrap : 1) Template cache ($templateCache) — évite les requêtes HTTP pendant le bootstrap. 2) Bootstrap manuel différé (angular.bootstrap()) après le chargement des ressources critiques. 3) Réduire les opérations lourdes dans .config()/.run(). 4) Bundling des fichiers JS. 5) $compileProvider.debugInfoEnabled(false).
Le Time To Interactive (TTI) inclut le bootstrap Angular. Profilez avec Chrome DevTools Performance pour identifier les fonctions lentes au bootstrap.

  • Ils sont identiques
  • angular.forEach() fonctionne aussi sur les objets (pas seulement les tableaux) et ne lève pas d'erreur si la collection est null (bonne réponse)
  • angular.forEach() est plus rapide que le forEach natif
  • angular.forEach() supporte la programmation asynchrone
Explication : angular.forEach(collection, fn) itère sur les tableaux ET les objets (clé/valeur). Il ne lève pas d'erreur si la collection est null ou undefined (itération sur 0 éléments). La callback reçoit (value, key). Moins performant que le for natif mais plus safe.

  • En utilisant $http.retry(3)
  • En créant un intercepteur HTTP qui re-exécute la requête originale sur responseError (bonne réponse)
  • En utilisant $httpProvider.defaults.retry = 3
  • Angular ne supporte pas le retry natif
Explication : Intercepteur retry : dans responseError, vérifier si le code d'erreur justifie un retry (503, 429), maintenir un compteur via la config de requête (config.retryCount), et si < max, re-exécuter avec $injector.get("$http")(config) ($http injecté lazily pour éviter la circularité). Retourner la rejection si épuisé.
Implémentez un backoff exponentiel ($timeout avec délai croissant) pour ne pas surcharger le serveur.

  • Un service pour créer des requêtes de template en cache
  • Un service qui charge les templates HTML et les stocke dans $templateCache (bonne réponse)
  • Un service pour valider les templates
  • Un service de préchargement de routes
Explication : $templateRequest(url) charge un template HTML depuis l'URL et le stocke dans $templateCache. Retourne une promise avec le HTML. Utilisé en interne par ng-include, les directives avec templateUrl, et le routeur. Utile pour précharger des templates dans .run().

  • C'est impossible sans les outils de dev Angular
  • En utilisant angular.element($0).scope() dans la console sur un élément sélectionné (bonne réponse)
  • En lisant directement les variables window.angular
  • En activant ng-debug dans la config
Explication : Depuis la console Chrome DevTools : 1) Sélectionner un élément DOM. 2) angular.element($0).scope() accède au scope de l'élément. 3) angular.element($0).injector() accède à l'injecteur. 4) angular.element($0).controller() accède au controller. Note : nécessite debugInfoEnabled(true) — désactivé par défaut en prod.
Activez temporairement le debug en production via la console : angular.reloadWithDebugInfo().

  • $http gère automatiquement les CSRF tokens sans configuration
  • AngularJS lit le cookie XSRF-TOKEN et l'envoie dans le header X-XSRF-TOKEN automatiquement (bonne réponse)
  • En ajoutant manuellement le token dans chaque requête
  • En utilisant ng-csrf sur les formulaires
Explication : AngularJS a une protection CSRF intégrée : il lit automatiquement le cookie XSRF-TOKEN et l'ajoute dans le header X-XSRF-TOKEN de chaque requête non-GET. Le serveur doit définir le cookie XSRF-TOKEN et vérifier le header X-XSRF-TOKEN. Les noms sont configurables via $httpProvider.defaults.xsrfCookieName.

  • AngularJS est plus rapide que jQuery dans tous les cas
  • AngularJS décrit QUOI afficher (état → vue) ; jQuery décrit COMMENT manipuler le DOM (étape par étape) (bonne réponse)
  • jQuery est déclaratif, AngularJS est impératif
  • Il n'y a pas de différence philosophique entre les deux
Explication : jQuery (impératif) : "Trouver l'élément, changer sa classe, mettre à jour son texte, masquer cet autre élément" — série d'instructions DOM. AngularJS (déclaratif) : "Voici mes données (état). Voici comment la vue doit être en fonction de cet état. Angular se charge du DOM." Le développeur pense état, pas manipulation DOM.
Cette distinction conceptuelle est fondamentale pour expliquer pourquoi AngularJS (et tous les frameworks modernes) existent. C'est souvent une question d'architecture en entretien senior.

  • Il active les fonctionnalités HTML5 dans AngularJS
  • Il active les URLs propres (sans #) en utilisant l'API History HTML5 au lieu des hash URLs (bonne réponse)
  • Il améliore les performances en utilisant des fonctionnalités HTML5
  • Il est activé par défaut dans AngularJS 1.6+
Explication : $locationProvider.html5Mode(true) dans .config() active les URLs propres (/route) via l'API history.pushState HTML5, au lieu des hash URLs (/#/route). Nécessite une configuration serveur pour rediriger toutes les URLs vers index.html.
Sans configuration serveur, les URLs propres donnent des 404 lors d'un rechargement direct. Configurer Nginx/Apache pour servir index.html pour toutes les routes est obligatoire.

  • Aucun opérateur JavaScript n'est supporté dans les templates
  • Les expressions AngularJS supportent les opérateurs arithmétiques, logiques, ternaires, comparaisons, mais pas les déclarations (var, for, while) (bonne réponse)
  • Tous les opérateurs JavaScript sont supportés sans restriction
  • Seuls les opérateurs de comparaison sont supportés
Explication : Les expressions AngularJS supportent : arithmétique (+, -, *, /, %), comparaison (==, !=, ===, !==, <, >), logique (&&, ||, !), ternaire (a ? b : c), accès aux propriétés (a.b, a[0]), appels de fonctions. Non supportés : déclarations de variables, boucles, throw/catch, accès à window/document.

  • Par ordre de déclaration dans le module
  • Par le nom du paramètre de la fonction (ou les annotations $inject / tableau) (bonne réponse)
  • Par le type de la variable (comme TypeScript)
  • Par l'ordre d'enregistrement des services
Explication : Angular analyse le nom des paramètres de la fonction (toString()) pour résoudre les dépendances. C'est pourquoi la minification (qui renomme les paramètres) casse l'injection. Les annotations $inject (tableau de noms en chaînes) contournent ce problème : MyCtrl.$inject = ["$scope", "$http"].

  • $resource met en cache toutes les requêtes par défaut
  • $resource ne met pas en cache par défaut — le cache doit être explicitement configuré (bonne réponse)
  • $resource met en cache uniquement les requêtes GET
  • $resource utilise le cache du navigateur automatiquement
Explication : Par défaut, $resource (comme $http) ne met pas les requêtes en cache. Pour activer : $resource(url, params, { get: { cache: true } }). Le cache est partagé avec $http — les deux utilisent le même $cacheFactory("$http").

  • En créant un module AngularJS avec ses propres services, directives et filtres (bonne réponse)
  • En ajoutant des propriétés sur angular directement
  • En créant un service global avec angular.service()
  • En utilisant angular.extend() pour ajouter des fonctionnalités
Explication : Une bibliothèque/plugin AngularJS est un module Angular qui expose ses propres services, directives, filtres et constants. Les applications l'incluent comme dépendance : angular.module("monApp", ["maLib"]). Exemples : angular-translate, angular-ui-router, ngMessages — tous sont des modules Angular.
Structure recommandée : un seul module principal + modules fonctionnels optionnels. Publier sur npm avec le keyword "angularjs" pour la découvrabilité.

  • En utilisant $rootScope.CONFIG = {} dans .run()
  • En créant un module séparé avec app.constant() injectable partout y compris .config() (bonne réponse)
  • En déclarant les constantes dans window
  • En utilisant localStorage
Explication : Pattern recommandé : angular.module("config", []).constant("APP_CONFIG", { apiUrl: "/api", timeout: 5000 }). Ce module est inclus comme dépendance principale. constant() est injectable dans .config() (contrairement à value()), ce qui permet de configurer les providers.

  • L'application utilise plus de 50MB de RAM
  • Lag perceptible à la frappe, animations saccadées, temps de réponse lents — souvent causés par trop de watchers ou des opérations lourdes dans le $digest (bonne réponse)
  • Les requêtes HTTP prennent plus de 500ms
  • La taille du bundle dépasse 1MB
Explication : Symptômes de problèmes $digest : 1) Lag à la frappe dans les inputs (chaque frappe déclenche $digest). 2) Animations saccadées (60fps = 16ms/frame, $digest lent casse ce budget). 3) Scroll saccadé avec ng-repeat. 4) Lenteur au chargement initial (trop de $watch initialisés). Diagnostic : compter les watchers, profiler le CPU.
Optimisations clés : one-time binding (::), track by, ng-if > ng-show, $watchCollection > $watch deep, déplacement de la logique des templates vers les contrôleurs.