{"pageProps":{"locale":{"code":"fr","domain":"frenchstack.fr","lang":"fr-FR","json":{"TRANSLATE":"Traduire en","EN":"Anglais","FR":"Français","NO_TRANSLATION_AVAILABLE":"Aucune traduction disponible","PROFILE_OF":"Profil de","FILTER_BY_TAG":"Filtrer par tag","REMOVE_FILTER":"Supprimer le filtre","RESULT":"Résulat","RESULTS":"Résulats","FORM_CONTACT_SUPPORT":"Support","FORM_CONTACT_SALES":"Devis","FORM_CONTACT_BUGS":"Rapport de bug","FORM_LOADING":"Chargement","FORM_SEND":"Envoyer","FORM_VERIFY":"Vérifier","FORM_NAME":"Nom","FORM_TOPIC":"Sujet","FORM_PHONE":"Téléphone","FORM_EMAIL":"Email","FORM_MESSAGE":"Message","FORM_DOMAIN_PLACEHOLDER":"exemple.fr","FORM_DOMAIN_AVAILABLE":"Félicitations, le domaine <span data-selector='domain'></span> est disponible !","FORM_DOMAIN_TAKEN":"Désolé, le domaine <span data-selector='domain'></span> est pris.","SUBSCRIBE":"S'abonner","SUCCESS_CONTACT":"Votre message a bien été envoyé ! Nous vous répondrons dans les plus brefs délais.","SOMETING_WENT_WRONG":"Oops, une erreur s'est produite. Veuillez réessayer ultérieurement.","PERFORMANCE":"Performance","ACCESSIBILITY":"Accessibilité","BEST_PRACTICES":"Bonnes pratiques","SEO":"Référencement","MOBILE":"Mobile","TABLET":"Tablette","DESKTOP":"Ordinateur","VISITORS":"Visiteurs","ENGAGEMENT":"Engagement","CONVERSIONS":"Conversions","JAN":"Janv","FEB":"Févr","MAR":"Mars","APR":"Avr","MAY":"Mai","JUN":"Juin","JUL":"Juil","AUG":"Août","SEP":"Sept","OCT":"Oct","NOV":"Nov","DEC":"Déc","METHOD_PHONE":"Téléphone","METHOD_EMAIL":"Email","FOOTER_TAGLINE":"Nous créons des expériences numériques uniques pour les marques et les entreprises."},"subdomains":{"blog":"blog","clients":"references","videos":"videos","services":"services"},"ogSection":"Technologie"},"slugs":["fr","blog","fallow-codebase-intelligence-typescript"],"component":"article","article":{"id":"content/articles/fr/fallow-codebase-intelligence-typescript.mdx","heroSection":{"heroTitle":"Fallow mesure, l'IA bâtit","heroSubtitle":"Le duo qui forme l'architecte logiciel ultime","heroBadge":"","heroColoredWords":"1,3","heroBreakTitle":"","heroDescription":{"type":"root","children":[{"type":"p","children":[{"type":"text","text":"D'un côté, une architecture verrouillée et mesurable. De l'autre, un agent qui exécute sans relâche."}]},{"type":"p","children":[{"type":"text","text":"Ensemble, ils découpent, nomment et rangent chaque ligne de code."}]}]},"heroSubtitleIsInline":false,"heroHasBackground":false,"heroImage":"/images/fallow-codebase-intelligence-typescript.png","heroVideoBackground":"","heroStyle":{"heroBg":"","heroMaxWidth":"","heroMs":"","heroTitleMb":"","heroTitleColor":"","heroContentBg":"","heroContentAlign":"","heroContentBlur":"","heroBlurImage":"","navbarBg":"","navbarBorderColor":"","navbarLinkActive":"","navbarLinkHover":""},"imageAlt":"fallow codebase intelligence typescript","imageWidth":1376,"imageHeight":768},"seoSection":{"metaTitle":"Fallow et agents IA : retour d'expérience sur la qualité de code","metaDescription":"Retour d'expérience : Fallow, analyseur statique TypeScript écrit en Rust, sert de source de vérité à mes agents IA et les guide pour découper fonctions et fichiers en unités claires, bien nommées et bien rangées.","metaTags":"Fallow, TypeScript, agent IA, qualité de code, complexité cognitive, refactoring, Claude Code, Codex, analyse statique, MCP, vibe coding","socialImageWidth":0,"socialImageHeight":0},"heroUpdatedAt":"2026-10-18T12:31:19.489Z","localeCode":"fr","slug":"fallow-codebase-intelligence-typescript","url":"/fr/blog/fallow-codebase-intelligence-typescript","ogId":"fallow-codebase-intelligence-typescript-blog-fr","ogImagePath":"/og/fallow-codebase-intelligence-typescript-blog-fr.png","type":"Article","title":"Fallow : la qualité de code au rythme des agents IA","subtitle":"Retour d'expérience sur un contrôle déterministe pour TypeScript","browserUrl":"","textPreview":{"type":"root","children":[{"type":"p","children":[{"type":"text","text":"Fallow mesure la complexité, le code mort et la duplication d'un projet TypeScript. Branché sur mes agents IA, il fait de chaque commit l'occasion de découper fonctions et fichiers en unités bien nommées et bien rangées."}]}]},"subdomainKey":"blog","subdomain":"blog","draft":false,"logoAlt":"","logoWidth":0,"logoHeight":0,"icon":"code","body":{"type":"root","children":[{"type":"p","children":[{"type":"text","text":"Les agents IA écrivent désormais une large part du code de mes projets. Ils livrent vite et bien avec une tendance constante : "},{"type":"text","text":"enrichir l'existant","bold":true},{"type":"text","text":" plutôt que de créer un nouveau concept. Au fil des demandes, une fonction de 20 lignes en compte 100, un fichier de 100 lignes en compte 500, et les noms ne décrivent plus qu'une partie de ce qu'ils contiennent."}]},{"type":"p","children":[{"type":"text","text":"Fallow","bold":true},{"type":"text","text":" corrige cette dérive avec une efficacité redoutable. Avant chaque commit, mon agent exécute Fallow pour mesurer les fonctions et les fichiers du projet : tant que le verdict n'est pas vert, l'IA découpe. Cette procédure garantit un code composé de petites fonctions, rangées dans des fichiers à responsabilité unique, dont le nom dit exactement ce qu'ils font."}]},{"type":"mdxJsxFlowElement","name":"codeTidy","children":[{"type":"text","text":""}],"props":{"before":"Un fichier qui fait tout","after":"Une responsabilité par fichier","caption":"Fallow mesure, l'agent découpe et range chaque responsabilité à sa place."}},{"type":"hr","children":[{"type":"text","text":""}]},{"type":"h2","children":[{"type":"text","text":"Fallow en bref"}]},{"type":"p","children":[{"type":"a","url":"https://fallow.tools","title":"Fallow - Codebase intelligence","children":[{"type":"text","text":"Fallow"}]},{"type":"text","text":" est un analyseur statique "},{"type":"text","text":"open source","italic":true},{"type":"text","text":" pour "},{"type":"text","text":"TypeScript et JavaScript","bold":true},{"type":"text","text":", distribué sous licence MIT sous la forme d'un binaire Rust unique. Il construit un graphe complet du dépôt (modules, exports, dépendances, fonctions) et en tire quatre familles d'analyses :"}]},{"type":"ul","children":[{"type":"li","children":[{"type":"lic","children":[{"type":"text","text":"Complexité","bold":true},{"type":"text","text":" : Mesure de la complexité cyclomatique et cognitive de chaque fonction, score de maintenabilité de chaque fichier et note de santé du projet sur 100."}]}]},{"type":"li","children":[{"type":"lic","children":[{"type":"text","text":"Code mort","bold":true},{"type":"text","text":" : Détection des fichiers, exports, types et dépendances que plus rien n'utilise."}]}]},{"type":"li","children":[{"type":"lic","children":[{"type":"text","text":"Duplication","bold":true},{"type":"text","text":" : Repérage des blocs copiés d'un fichier à l'autre, dans le code TypeScript et JavaScript comme dans les feuilles de style et les composants d'interface."}]}]},{"type":"li","children":[{"type":"lic","children":[{"type":"text","text":"Architecture","bold":true},{"type":"text","text":" : Contrôle du découpage des imports (interface, métier, données) et détection des dépendances circulaires."}]}]}]},{"type":"p","children":[{"type":"text","text":"L'analyseur n'embarque "},{"type":"text","text":"aucun modèle de langage","bold":true},{"type":"text","text":" : la même entrée produit toujours le même résultat. Il s'exécute seul, sans compilateur TypeScript ni runtime Node.js. Selon les mesures publiées par le projet, il est capable d'analyser un dépôt aussi volumineux que le monorepo de Next.js ("},{"type":"text","text":"20 558 fichiers","bold":true},{"type":"text","text":") en moins de "},{"type":"text","text":"3 secondes","bold":true},{"type":"text","text":"."}]},{"type":"p","children":[{"type":"text","text":"Ce déterminisme en fait le bon partenaire d'un agent IA : l'agent propose, Fallow mesure."}]},{"type":"mdxJsxFlowElement","name":"sourceNote","children":[{"type":"text","text":""}],"props":{"content":{"type":"root","children":[{"type":"p","children":[{"type":"text","text":"Sources : "},{"type":"a","url":"https://github.com/fallow-rs/fallow/blob/main/BENCHMARKS.md","title":null,"children":[{"type":"text","text":"BENCHMARKS.md"}]},{"type":"text","text":" du projet (mesures de juin 2026) et "},{"type":"a","url":"https://fallow.tools","title":null,"children":[{"type":"text","text":"fallow.tools"}]},{"type":"text","text":", consultés en octobre 2026."}]}]}}},{"type":"hr","children":[{"type":"text","text":""}]},{"type":"h2","children":[{"type":"text","text":"Un arbitre dans la boucle de l'agent"}]},{"type":"p","children":[{"type":"text","text":"Au rythme des agents, je ne relis plus chaque ligne une à une. Demander à un agent « d'écrire du code simple » reste une intention : sans mesure, il n'a aucun moyen de savoir si la contrainte est respectée."}]},{"type":"p","children":[{"type":"text","text":"Fallow devient le garde-fou de chaque commit. Le schéma ci-dessous montre la boucle que suit l'agent pour chaque tâche :"}]},{"type":"mdxJsxFlowElement","name":"agentLoop","children":[{"type":"text","text":""}],"props":{"steps":[{"actor":"agent","title":"Développement","detail":"Implémentation de la demande, sans contrainte particulière"},{"actor":"fallow","title":"Mesure","detail":"Signalement des fonctions trop complexes, des fichiers à scinder, des exports inutiles et des blocs dupliqués","inLoop":true},{"actor":"agent","title":"Découpage","detail":"Extraction des étapes en fonctions nommées et des responsabilités en fichiers dédiés","inLoop":true},{"actor":"git","title":"Commit","detail":"Accepté seulement sur un verdict « pass »"}],"loopLabel":"jusqu'au vert","caption":"Le verrou bloque le commit tant que Fallow rend un verdict « fail »."}},{"type":"p","children":[{"type":"text","text":"Le découpage s'opère à deux échelles :"}]},{"type":"ul","children":[{"type":"li","children":[{"type":"lic","children":[{"type":"text","text":"Fonction","bold":true},{"type":"text","text":" : Seuils par défaut de "},{"type":"text","text":"20","bold":true},{"type":"text","text":" en complexité cyclomatique, "},{"type":"text","text":"15","bold":true},{"type":"text","text":" en complexité cognitive et "},{"type":"text","text":"60 lignes","bold":true},{"type":"text","text":" par fonction, que je conserve tels quels."}]}]},{"type":"li","children":[{"type":"lic","children":[{"type":"text","text":"Fichier","bold":true},{"type":"text","text":" : Score de maintenabilité calculé à partir de la taille, de la densité de complexité, du code mort et des dépendances, et recommandations classées par priorité, comme « scinder un fichier dont trop d'autres dépendent » ou « extraire la fonction "},{"type":"text","text":"parseRow","code":true},{"type":"text","text":" d'un fichier de 520 lignes »."}]}]}]},{"type":"p","children":[{"type":"text","text":"Fallow ne dit pas comment corriger : il désigne ce qui est trop long, trop imbriqué, trop central ou répété, et laisse à l'agent le soin de "},{"type":"text","text":"découper et ranger","bold":true},{"type":"text","text":"."}]},{"type":"mdxJsxFlowElement","name":"sourceNote","children":[{"type":"text","text":""}],"props":{"content":{"type":"root","children":[{"type":"p","children":[{"type":"text","text":"Source : "},{"type":"a","url":"https://fallow.tools/docs/explanations/health/","title":null,"children":[{"type":"text","text":"Health explained"}]},{"type":"text","text":", documentation de Fallow consultée en octobre 2026."}]}]}}},{"type":"hr","children":[{"type":"text","text":""}]},{"type":"h2","children":[{"type":"text","text":"Retour d'expérience"}]},{"type":"p","children":[{"type":"text","text":"J'ai codé un petit bot en "},{"type":"text","text":"TypeScript","bold":true},{"type":"text","text":" qui réserve automatiquement mes séances de sport, dont les places partent en quelques minutes. Une fois stabilisé, j'ai confié le projet à un agent IA pour qu'il l'intègre à "},{"type":"text","text":"Home Assistant","bold":true},{"type":"text","text":" : interface graphique, réservations pilotables et notifications sur le téléphone."}]},{"type":"p","children":[{"type":"img","url":"/images/fallow-rpm-lesmills-bot.png","alt":"Interface du bot de réservation de cours de sport dans Home Assistant","caption":"1448x836","children":[{"type":"text","text":""}]}]},{"type":"p","children":[{"type":"text","text":"L'agent a livré rapidement un projet fonctionnel, mais chaque évolution s'était greffée sur le code existant. Une seule fonction contenait la connexion à l'API, la récupération du planning, le choix du créneau puis la réservation, et toute l'interface tenait dans un seul fichier."}]},{"type":"p","children":[{"type":"text","text":"Une fois Fallow branché, l'agent n'a pas coupé au hasard pour faire baisser un chiffre : il a isolé chaque "},{"type":"text","text":"étape métier","bold":true},{"type":"text","text":" dans une fonction dédiée, avec un nom qui dit ce qu'elle fait, puis a appliqué "},{"type":"text","text":"le même découpage par responsabilité","bold":true},{"type":"text","text":" à l'échelle des fichiers."}]},{"type":"p","children":[{"type":"text","text":"Le résultat se mesure sur trois parties du projet :"}]},{"type":"ul","children":[{"type":"li","children":[{"type":"lic","children":[{"type":"text","text":"Réservation","bold":true},{"type":"text","text":" : La fonction principale passe de "},{"type":"text","text":"88 à 28 lignes","bold":true},{"type":"text","text":" et délègue la connexion, le planning, le choix du créneau et la réservation à "},{"type":"text","text":"4 fonctions","bold":true},{"type":"text","text":" nommées d'après leur étape."}]}]},{"type":"li","children":[{"type":"lic","children":[{"type":"text","text":"Interface","bold":true},{"type":"text","text":" : Le fichier "},{"type":"text","text":"App.tsx","code":true},{"type":"text","text":" passe de "},{"type":"text","text":"501 à 116 lignes","bold":true},{"type":"text","text":", réparti en "},{"type":"text","text":"7 composants","bold":true},{"type":"text","text":" réutilisables, un fichier de types partagés et un "},{"type":"text","text":"hook","italic":true},{"type":"text","text":" dédié."}]}]},{"type":"li","children":[{"type":"lic","children":[{"type":"text","text":"Notifications","bold":true},{"type":"text","text":" : L'envoi des notifications, écrit "},{"type":"text","text":"3 fois","bold":true},{"type":"text","text":", n'existe plus qu'à un seul endroit sous le nom "},{"type":"text","text":"notifyBookingSuccess","code":true},{"type":"text","text":"."}]}]}]},{"type":"hr","children":[{"type":"text","text":""}]},{"type":"h2","children":[{"type":"text","text":"Une source de vérité pour cadrer l'agent"}]},{"type":"p","children":[{"type":"text","text":"Un agent IA intervient sur un projet comme le ferait un développeur : il cherche le bon fichier, lit ce qui l'entoure et modifie ce qu'il comprend. Un projet généré prompt après prompt, sans jamais être refactoré, accumule dès les premières demandes les traits d'un code "},{"type":"text","text":"spaghetti","italic":true},{"type":"text","text":" : fichiers aux responsabilités mêlées, noms approximatifs, logique dupliquée. Dans ce contexte, chaque action de l'agent se complique : trouver le bon fichier l'oblige à en lire davantage, et modifier ce qu'il comprend ne suffit plus, car les effets de bord se multiplient."}]},{"type":"p","children":[{"type":"text","text":"Fallow inverse cette pente sans exiger une expertise en architecture logicielle. Les règles de découpage, de nommage et de rangement n'ont plus à être détaillées dans chaque prompt, ni contrôlées à la relecture : elles sont mesurées, et le verdict s'impose à l'agent. Le bénéfice se lit sur trois plans :"}]},{"type":"mdxJsxFlowElement","name":"benefitCards","children":[{"type":"text","text":""}],"props":{"items":[{"icon":"shield","tone":"success","title":"Moins de régressions","text":"Des fonctions courtes et des fichiers à responsabilité unique, où chaque modification reste circonscrite à la partie concernée."},{"icon":"coins","tone":"primary","title":"Moins de jetons","text":"Une navigation guidée par des noms explicites, sans relecture de fichiers entiers pour trouver la bonne ligne."},{"icon":"target","tone":"warning","title":"Plus de maîtrise","text":"Un critère objectif et reproductible pour valider le travail de l'agent, là où une consigne en langage naturel reste invérifiable."}]}},{"type":"p","children":[{"type":"text","text":"Confiez Fallow à votre agent : à chaque commit, il découpe, nomme et range le code sans que vous ayez à lui dicter chaque règle. Vous gardez un projet lisible, moins coûteux à faire évoluer, et chaque nouvelle demande part d'une base saine. La qualité ne se décrète plus, elle se vérifie."}]},{"type":"mdxJsxFlowElement","name":"buttons","children":[{"type":"text","text":""}],"props":{"buttons":[{"icon":"gitHub","label":"Fallow sur GitHub","link":"https://github.com/fallow-rs/fallow","style":"primary"},{"icon":"codeFile","label":"Documentation","link":"https://fallow.tools/docs","style":"light"}]}}]},"dateCreate":"2026-10-18T09:00:00.000Z","dateUpdate":"2026-10-18T09:00:00.000Z","author":{"id":"content/authors/fr/nicolas-roehm-bettcher.mdx","heroSection":{"heroTitle":"Nicolas Roehm","heroSubtitle":"Ingénieur logiciel","heroBadge":"","heroColoredWords":"","heroBreakTitle":"","heroDescription":{"type":"root","children":[{"type":"p","children":[{"type":"text","text":"Fondateur de French Stack, je conçois des solutions informatiques complètes."}]},{"type":"p","children":[{"type":"text","text":"Développeur full-stack, j'interviens principalement sur des projets frontend avec Angular, React ou Vue."}]},{"type":"p","children":[{"type":"text","text":"Passionné de design, voici quelques-unes de mes "},{"type":"a","url":"https://www.artstation.com/nicolas-roehm","title":"ArtStation - Nicolas Roehm","children":[{"type":"text","text":"conceptions graphiques"}]},{"type":"text","text":"."}]},{"type":"mdxJsxFlowElement","name":"socialLinks","children":[{"type":"text","text":""}],"props":{"links":[{"icon":"gitHub","link":"https://github.com/NicolasRoehm"},{"icon":"stackOverflow","link":"https://stackoverflow.com/users/7462178/nicolas-roehm?tab=profile"},{"icon":"linkedIn","link":"https://fr.linkedin.com/in/nicolas-roehm-bettcher"}]}}]},"heroSubtitleIsInline":true,"heroHasBackground":false,"heroImage":"/images/nicolas-roehm-bettcher.png","heroVideoBackground":"","heroStyle":{"heroBg":"linear-gradient(312deg, #0800ff 0%, #4300ff00 70%)","heroMaxWidth":"","heroMs":"25%","heroTitleMb":"0.5rem","heroTitleColor":"","heroContentBg":"","heroContentAlign":"","heroContentBlur":"","heroBlurImage":"","navbarBg":"#fff0","navbarBorderColor":"","navbarLinkActive":"#6785be","navbarLinkHover":"#6785be"},"imageAlt":"nicolas roehm bettcher","imageWidth":0,"imageHeight":0},"seoSection":{"metaTitle":"Nicolas Roehm, Ingénieur logiciel","metaDescription":"Passionné d'informatique, je conçois des sites web dans leur intégralité : design, developpement, référencement, e-reputation, e-mailling, hébergement","metaTags":"Nicolas Roehm, Ingénieur logiciel, web, mobile, site internet","socialImageWidth":0,"socialImageHeight":0},"heroUpdatedAt":"2024-12-09T00:38:50.025Z","localeCode":"fr","slug":"nicolas-roehm-bettcher","url":"/fr/nicolas-roehm-bettcher","ogId":"nicolas-roehm-bettcher-fr","ogImagePath":"/og/nicolas-roehm-bettcher-fr.png","type":"Author","title":"Nicolas Roehm-Bettcher","avatar":"/images/nicolas-roehm-avatar.png","lname":"Roehm","fname":"Nicolas","name":"Nicolas Roehm","translations":[{"localeCode":"en","title":"Nicolas Roehm-Bettcher","slug":"nicolas-roehm-bettcher","url":"/en/nicolas-roehm-bettcher"}]},"translations":[],"tags":[{"id":"content/tags/fr/typescript.mdx","localeCode":"fr","title":"TypeScript","slug":"typescript","url":"/fr/blog/typescript"}]},"page":{"id":"content/subdomains/fr/blog.mdx","heroSection":{"heroTitle":"Actualités digitales","heroSubtitle":"","heroBadge":"","heroColoredWords":"2","heroBreakTitle":"","heroShape":"shiny","heroGradient":"business-gradient","heroDescription":{"type":"root","children":[{"type":"p","children":[{"type":"text","text":"Explorez les dernières avancées en "},{"type":"text","text":"design web","bold":true},{"type":"text","text":", "},{"type":"text","text":"SEO","bold":true},{"type":"text","text":", "},{"type":"text","text":"marketing digital","bold":true},{"type":"text","text":", "},{"type":"text","text":"développement web","bold":true},{"type":"text","text":" et bien plus."}]},{"type":"p","children":[{"type":"text","text":"Nos articles décryptent les "},{"type":"text","text":"technologies innovantes","bold":true},{"type":"text","text":" et les "},{"type":"text","text":"standards du web","bold":true},{"type":"text","text":" qui façonnent l'"},{"type":"text","text":"avenir du numérique","bold":true},{"type":"text","text":"."}]},{"type":"p","children":[{"type":"text","text":"Restez connecté, suivez-nous sur les "},{"type":"text","text":"réseaux sociaux","bold":true},{"type":"text","text":"."}]}]},"heroSubtitleIsInline":false,"heroHasBackground":false,"heroImage":"/images/blog-news-web.jpg","heroVideoBackground":"","heroStyle":{"heroBg":"linear-gradient(353deg, rgba(255,129,0,0.64) 0%, #0018ffe0 120%)","heroMaxWidth":"","heroMs":"","heroTitleMb":"","heroTitleColor":"","heroContentBg":"","heroContentAlign":"","heroContentBlur":"","heroBlurImage":"","navbarBg":"","navbarBorderColor":"","navbarLinkActive":"","navbarLinkHover":""},"imageAlt":"blog news web","imageWidth":0,"imageHeight":0},"seoSection":{"metaTitle":"Actualité numérique","metaDescription":"Magazine en ligne pour apprendre le Web, trouver des ressources, parcourir les tendances et déguster un café chaud","metaTags":"inspiration web, tutoriels développement web, tendances web","socialImageWidth":0,"socialImageHeight":0},"heroUpdatedAt":"2024-12-12T18:23:51.381Z","localeCode":"fr","slug":"blog","url":"/fr/blog","ogId":"blog-fr","ogImagePath":"/og/blog-fr.png","type":"Subdomain","title":"Blog","subdomainKey":"blog","subdomain":"blog","translations":[{"localeCode":"en","title":"Blog","slug":"blog","url":"/en/blog"}],"canShare":true,"canComment":true,"hasAuthor":true,"tagsInPanel":true,"cardContainer":"col-md-6","cardTemplate":"default"},"neighbors":[null,{"id":"content/articles/fr/angular-validation-formulaire-comparer-plusieurs-champs.mdx","heroSection":{"heroTitle":"Comparer plusieurs champs","heroSubtitle":"","heroBadge":"Validation de formulaire Angular","heroColoredWords":"1","heroBreakTitle":"","heroDescription":{"type":"root","children":[{"type":"p","children":[{"type":"text","text":"Comment valider deux champs d'un formulaire en faisant une comparaison ?"}]},{"type":"p","children":[{"type":"text","text":"Vérifions plusieurs champs avec un validateur de haut niveau !"}]}]},"heroSubtitleIsInline":false,"heroHasBackground":false,"heroImage":"/images/angular-form-validation.png","heroVideoBackground":"","heroStyle":{"heroBg":"linear-gradient(180deg, transparent 0%, #0e256ddb 17%, #ff4444db 120%)","heroMaxWidth":"","heroMs":"","heroTitleMb":"","heroTitleColor":"","heroContentBg":"","heroContentAlign":"","heroContentBlur":"","heroBlurImage":"","navbarBg":"","navbarBorderColor":"","navbarLinkActive":"","navbarLinkHover":""},"imageAlt":"angular form validation","imageWidth":1344,"imageHeight":768},"seoSection":{"metaTitle":"Validation de formulaire Angular : Comparer plusieurs champs","metaDescription":"Comment valider deux champs d'un formulaire en faisant une comparaison ? Vérifions plusieurs champs avec un validateur de haut niveau !","metaTags":"formulaire, angular, validation, angular-validation","socialImageWidth":0,"socialImageHeight":0},"heroUpdatedAt":"2024-12-07T00:09:54.046Z","localeCode":"fr","slug":"angular-validation-formulaire-comparer-plusieurs-champs","url":"/fr/blog/angular-validation-formulaire-comparer-plusieurs-champs","ogId":"angular-validation-formulaire-comparer-plusieurs-champs-blog-fr","ogImagePath":"/og/angular-validation-formulaire-comparer-plusieurs-champs-blog-fr.png","type":"Article","title":"Comparer plusieurs champs avec Angular","subtitle":"","browserUrl":"","textPreview":{"type":"root","children":[]},"subdomainKey":"blog","subdomain":"blog","draft":false,"logoAlt":"","logoWidth":0,"logoHeight":0,"body":{"type":"root","children":[{"type":"p","children":[{"type":"text","text":"Supposons que notre formulaire ait des champs de date "},{"type":"text","text":"startDate","code":true},{"type":"text","text":" et "},{"type":"text","text":"endDate","code":true},{"type":"text","text":" et nous voulons nous assurer que "},{"type":"text","text":"startDate","code":true},{"type":"text","text":" doit être avant "},{"type":"text","text":"endDate","code":true},{"type":"text","text":"."}]},{"type":"p","children":[{"type":"text","text":"Pour implémenter des validations contenant un ou plusieurs contrôles frères, nous devons définir la fonction de validation à un niveau supérieur à celui des contrôles frères."}]},{"type":"mdxJsxFlowElement","name":"codeHeader","children":[{"type":"text","text":""}],"props":{"filePath":"app / src / pages / form-example / form-example.component.ts","fileName":"form-example.component.ts","svgFile":"typescript"}},{"type":"code_block","lang":"typescript","value":"ngOnInit() : void\n{\n  this.formGroup = this.formBuilder.group(\n  {\n    startDate : new FormControl({\n      value    : null,\n      disabled : false\n    }, [ Validators.required ]),\n    endDate : new FormControl({\n      value    : null,\n      disabled : false\n    }, [ Validators.required ]),\n  },\n  // NOTE Validateur de haut niveau\n  { validator : DateValidator.dateRangeValidator });\n}","children":[{"type":"text","text":""}]},{"type":"p","children":[{"type":"text","text":"Ensuite, nous définissons un validateur personnalisé dans un fichier séparé, par exemple "},{"type":"text","text":"date.validator.ts","code":true},{"type":"text","text":"."}]},{"type":"mdxJsxFlowElement","name":"codeHeader","children":[{"type":"text","text":""}],"props":{"fileName":"date.validator.ts","filePath":"app / src / shared / validators / date.validator.ts","svgFile":"typescript"}},{"type":"code_block","lang":"typescript","value":"// Angular modules\nimport { Injectable }       from '@angular/core';\nimport { AbstractControl }  from '@angular/forms';\nimport { ValidationErrors } from '@angular/forms';\n\n@Injectable({\n  providedIn : 'root'\n})\nexport class DateValidator\n{\n  constructor() {}\n\n  public static dateRangeValidator(control : AbstractControl) : ValidationErrors | null\n  {\n    // NOTE Contrôle de sécurité\n    if (!control.get('startDate').value || !control.get('endDate').value)\n      return null;\n\n    // NOTE Comparaison de champs\n    // const mStart  = moment(control.get('startDate').value);\n    // const mEnd    = moment(control.get('endDate').value);\n    // const isValid = mStart.isBefore(mEnd);\n\n    // NOTE Invalide\n    if (!isValid)\n      return { invalidDateRange : true };\n\n    // NOTE Valide\n    return null;\n  }\n}","children":[{"type":"text","text":""}]},{"type":"p","children":[{"type":"text","text":"Si invalide, retourne un objet d'erreur (n'importe quel nom arbitraire), comme, "},{"type":"text","text":"return { invalidEndDate: true }","code":true},{"type":"text","text":"."}]},{"type":"p","children":[{"type":"text","text":"Assurez-vous qu'il renvoie toujours un "},{"type":"text","text":"null","code":true},{"type":"text","text":" pour les cas valides ou non pertinents, et un objet \"non-null\" dans les cas où une erreur doit être déclenchée sur le "},{"type":"text","text":"formGroup","code":true},{"type":"text","text":"."}]},{"type":"p","children":[{"type":"text","text":"Pour afficher l'erreur au niveau de notre vue, nous utilisons la clé "},{"type":"text","text":"invalidDateRange","code":true},{"type":"text","text":" retournée par notre validateur."}]},{"type":"mdxJsxFlowElement","name":"codeHeader","children":[{"type":"text","text":""}],"props":{"fileName":"form-example.component.html","filePath":"app / src / pages / form-example / form-example.component.html","svgFile":"html"}},{"type":"code_block","lang":"html","value":"<div *ngIf=\"formGroup.hasError('invalidDateRange')\">\n  La date de début n'est pas antérieure à la date de fin\n</div>","children":[{"type":"text","text":""}]}]},"dateCreate":"2021-03-17T10:46:42.000Z","dateUpdate":"2021-09-15T10:57:26.000Z","author":{"id":"content/authors/fr/nicolas-roehm-bettcher.mdx","heroSection":{"heroTitle":"Nicolas Roehm","heroSubtitle":"Ingénieur logiciel","heroBadge":"","heroColoredWords":"","heroBreakTitle":"","heroDescription":{"type":"root","children":[{"type":"p","children":[{"type":"text","text":"Fondateur de French Stack, je conçois des solutions informatiques complètes."}]},{"type":"p","children":[{"type":"text","text":"Développeur full-stack, j'interviens principalement sur des projets frontend avec Angular, React ou Vue."}]},{"type":"p","children":[{"type":"text","text":"Passionné de design, voici quelques-unes de mes "},{"type":"a","url":"https://www.artstation.com/nicolas-roehm","title":"ArtStation - Nicolas Roehm","children":[{"type":"text","text":"conceptions graphiques"}]},{"type":"text","text":"."}]},{"type":"mdxJsxFlowElement","name":"socialLinks","children":[{"type":"text","text":""}],"props":{"links":[{"icon":"gitHub","link":"https://github.com/NicolasRoehm"},{"icon":"stackOverflow","link":"https://stackoverflow.com/users/7462178/nicolas-roehm?tab=profile"},{"icon":"linkedIn","link":"https://fr.linkedin.com/in/nicolas-roehm-bettcher"}]}}]},"heroSubtitleIsInline":true,"heroHasBackground":false,"heroImage":"/images/nicolas-roehm-bettcher.png","heroVideoBackground":"","heroStyle":{"heroBg":"linear-gradient(312deg, #0800ff 0%, #4300ff00 70%)","heroMaxWidth":"","heroMs":"25%","heroTitleMb":"0.5rem","heroTitleColor":"","heroContentBg":"","heroContentAlign":"","heroContentBlur":"","heroBlurImage":"","navbarBg":"#fff0","navbarBorderColor":"","navbarLinkActive":"#6785be","navbarLinkHover":"#6785be"},"imageAlt":"nicolas roehm bettcher","imageWidth":0,"imageHeight":0},"seoSection":{"metaTitle":"Nicolas Roehm, Ingénieur logiciel","metaDescription":"Passionné d'informatique, je conçois des sites web dans leur intégralité : design, developpement, référencement, e-reputation, e-mailling, hébergement","metaTags":"Nicolas Roehm, Ingénieur logiciel, web, mobile, site internet","socialImageWidth":0,"socialImageHeight":0},"heroUpdatedAt":"2024-12-09T00:38:50.025Z","localeCode":"fr","slug":"nicolas-roehm-bettcher","url":"/fr/nicolas-roehm-bettcher","ogId":"nicolas-roehm-bettcher-fr","ogImagePath":"/og/nicolas-roehm-bettcher-fr.png","type":"Author","title":"Nicolas Roehm-Bettcher","avatar":"/images/nicolas-roehm-avatar.png","lname":"Roehm","fname":"Nicolas","name":"Nicolas Roehm","translations":[{"localeCode":"en","title":"Nicolas Roehm-Bettcher","slug":"nicolas-roehm-bettcher","url":"/en/nicolas-roehm-bettcher"}]},"translations":[{"localeCode":"en","title":"Comparing Multiple Fields with Angular","slug":"angular-form-validation-compare-multiple-fields","url":"/en/blog/angular-form-validation-compare-multiple-fields"}],"tags":[{"id":"content/tags/fr/angular.mdx","localeCode":"fr","title":"Angular","slug":"angular","url":"/fr/blog/angular"}]}],"global":{"id":"content/global/fr/settings.json","localeCode":"fr","header":{"nav":[{"href":"index","label":"Accueil"},{"href":"blog","label":"Blog"},{"href":"videos","label":"Vidéos"},{"href":"services","label":"Prestations"},{"href":"references","label":"Références"},{"href":"a-propos","label":"À propos"},{"href":"contact","label":"Contact"}]},"footer":{"social":[{"website":"x","account":"RoehmNicolas"},{"website":"instagram","account":"roehm.nicolas"},{"website":"facebook","account":"thefrenchstack"},{"website":"youtube","account":"@frenchstack"}],"hasSocial":true,"nav":[{"title":"L'agence","links":[{"href":"references","label":"Références"},{"href":"a-propos","label":"À propos"},{"href":"contact","label":"Contact"}]},{"title":"Prestations","links":[{"href":"services/creation-graphique","label":"Graphisme"},{"href":"services/developpement-web-mobile","label":"Développement"},{"href":"services/marketing-web","label":"Marketing"},{"href":"services/formation-informatique","label":"Formation"}]},{"title":"Blog","links":[{"href":"blog/angular","label":"Angular"},{"href":"blog/jquery","label":"jQuery"}]},{"title":"Vidéos","links":[{"href":"videos/marketing-audiovisuel","label":"Marketing audiovisuel"}]}]},"bottom":{"dateCreate":"2015-12-27T05:55:00.000Z","nav":[{"href":"mentions-legales","label":"Mentions légales"},{"href":"politique-de-confidentialite","label":"Politique de confidentialité"}],"hasLinks":true},"contactInfo":{"contactMethods":[{"id":"email","icon":"envelope","value":"frenchstack@gmail.com"},{"id":"phone","icon":"phone","value":"0651570751"}]}}},"__N_SSG":true}