Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
Angular

Le property binding dans angular

La liaison de propriété (property binding) transmet une donnée de la classe vers le template, à sens unique, en suivant automatiquement les signals. Ce chapitre en détaille tous les usages avec Angula...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À propos de ce cours

La liaison de propriété (property binding) transmet une donnée de la classe vers le template, à sens unique, en suivant automatiquement les signals. Ce chapitre en détaille tous les usages avec Angular 22 : l’anatomie d’une liaison : une cible entre crochets et une expression TypeScript entre guillemets ; la différence entre crochets, interpolation et texte fixe, et le piège des booléens (disabled="{{ false }}" désactive quand même le bouton) ; les propriétés natives : [src], [disabled], [type], [readonly], [maxLength], [href], [tabIndex]… et la correspondance entre noms d’attributs et noms de propriétés ; la liaison d’attribut [attr.…] pour data-*, role, ARIA et SVG, et le rôle de null pour retirer un attribut ; les entrées d’un composant avec input(), numberAttribute et booleanAttribute, vérifiées à la compilation ; les bonnes pratiques avec les signals et computed() ; la sécurité : le nettoyage automatique des URL et du HTML dangereux, et le bon usage de DomSanitizer.
  1. Objectifs

    • Comprendre l'anatomie d'une liaison de propriété : une cible entre crochets et une expression entre guillemets
    • Choisir entre les crochets, l'interpolation et un texte fixe, et éviter le piège des booléens
    • Lier les propriétés natives des éléments HTML : formulaires, liens, médias, accessibilité
    • Lier des attributs qui n'ont pas de propriété (data-*, role, ARIA, SVG) avec [attr.…]
    • Fournir des valeurs aux entrées d'un composant, avec leur vérification de type
    • Connaître la protection automatique d'Angular contre les valeurs dangereuses
  1. Présentation

    • Dans le chapitre « Le data binding dans Angular », vous avez vu que la liaison de propriété transmet une donnée de la classe vers le template. C'est la liaison la plus utilisée : elle sert à régler une image, désactiver un bouton, remplir un champ, colorer une ligne ou configurer un composant enfant.
    • Sa syntaxe est [cible]="expression":
      • la cible, entre crochets, est ce qui reçoit la valeur : une propriété du DOM, l'entrée d'un composant, un attribut ou une classe CSS ;
      • l'expression, entre guillemets, est du TypeScript évalué dans le contexte de la classe : photo().url, !valide(), 'Logo de ' + ecole.
    • La liaison est à sens unique: le template lit la donnée, il ne la modifie jamais. Si l'expression lit un signal, Angular met la cible à jour dès que le signal change.

Anatomie d'une liaison de propriété et ses quatre sortes de cibles

  • Pour suivre ce chapitre, continuez dans le projet premiers-pas-angular et créez un dossier src/app/property/. Placez une image formation.svg dans le dossier public/. Tous les exemples ont été testés avec Angular 22.
  1. Crochets, interpolation ou texte fixe ?

    • Une valeur peut s'écrire de trois façons. Seuls les crochets transmettent la valeur avec son vrai type; l'interpolation dans un attribut la convertit toujours en chaîne; sans crochets ni accolades, c'est un simple texte.

Crochets, interpolation et texte fixe : la valeur reçue

import { Component } from '@angular/core';

@Component({
  selector: 'app-ecritures',
  template: `
    <!-- 1. Crochets : la valeur est une expression TypeScript -->
    <img [src]="logo" [alt]="'Logo de ' + ecole" width="48" />
    <input [value]="quantite" [disabled]="verrouille" />

    <!-- 2. Interpolation dans un attribut : la valeur devient une chaîne -->
    <img src="{{ logo }}" alt="Logo de {{ ecole }}" width="48" />
    <input value="{{ quantite }}" disabled="{{ verrouille }}" />

    <!-- 3. Sans crochets ni interpolation : un simple texte fixe -->
    <img src="formation.svg" alt="ecole" width="48" />
  `,
})
export class Ecritures {
  protected readonly logo = 'formation.svg';
  protected readonly ecole = 'APC Pédagogie';
  protected readonly quantite = 3;
  protected readonly verrouille = false;
}
<img width="48" src="formation.svg" alt="Logo de APC Pédagogie">
<input>                    ← actif, value = "3" (propriété, invisible dans le HTML)
<img width="48" src="formation.svg" alt="Logo de APC Pédagogie">
<input disabled="">        ← désactivé alors que verrouille vaut false !
<img src="formation.svg" alt="ecole" width="48">
  • Les deux premières images sont identiques : pour une chaîne, crochets et interpolation donnent le même résultat. L'interpolation est souvent plus lisible pour composer un texte : alt="Logo de {{ ecole }}".
  • Pour un booléen ou un nombre, utilisez toujours les crochets. disabled="{{ verrouille }}" transmet la chaîne "false", qui est une valeur « vraie » : le champ est désactivé.
  • Dans la troisième image, alt="ecole" affiche le mot « ecole » : sans crochets, le nom de la propriété n'est pas évalué. Inversement, pour un texte fixe entre crochets, il faut des apostrophes : [alt]="'Logo'".
  • La propriété value du premier champ ne figure pas dans le HTML inspecté : une liaison de propriété modifie l'objet du DOM, pas le texte HTML. Pour la voir, sélectionnez l'élément dans l'inspecteur puis tapez $0.value dans la console.
  1. Les propriétés natives des éléments HTML

    • Chaque élément HTML possède de nombreuses propriétés que l'on peut lier : src, href, disabled, value, checked, type, placeholder, title, hidden…
    • Le nom d'une propriété diffère parfois de celui de l'attribut (readonly → readOnly). Angular accepte les deux écritures pour les cas courants.

Correspondance entre noms d'attributs HTML et propriétés du DOM

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

@Component({
  selector: 'app-fiche-contact',
  template: `
    <label [for]="'email-' + id">E-mail</label>
    <input [id]="'email-' + id" [type]="'email'" [value]="email()" [readonly]="verrouille()"
           [maxLength]="40" [placeholder]="verrouille() ? '' : 'nom@domaine.tn'" />

    <label [for]="'code-' + id">Code d'accès</label>
    <input [id]="'code-' + id" [type]="codeVisible() ? 'text' : 'password'" value="angular22" />
    <button type="button" (click)="codeVisible.set(!codeVisible())" [title]="codeVisible() ? 'Masquer' : 'Afficher'">
      {{ codeVisible() ? '🙈' : '👁' }}
    </button>

    <label><input type="checkbox" [checked]="verrouille()" (change)="verrouille.set(!verrouille())" /> Verrouiller</label>
    <p><a [href]="lienCourriel()" [target]="'_blank'" [tabIndex]="verrouille() ? -1 : 0">Écrire à {{ email() }}</a></p>
    <meter [value]="remplissage" [min]="0" [max]="100" [low]="40" [high]="80" [optimum]="100"></meter>
  `,
})
export class FicheContact {
  protected readonly id = 7;
  protected readonly email = signal('amira@apcpedagogie.com');
  protected readonly verrouille = signal(true);
  protected readonly codeVisible = signal(false);
  protected readonly remplissage = 65;
  protected readonly lienCourriel = computed(() => `mailto:${this.email()}?subject=${encodeURIComponent('Cours Angular')}`);
}
<label for="email-7">E-mail</label>
<input id="email-7" type="email" readonly="" maxlength="40" placeholder="">
<a href="mailto:amira@apcpedagogie.com?subject=Cours%20Angular" target="_blank" tabindex="-1">Écrire à amira@apcpedagogie.com</a>
<meter value="65" min="0" max="100" low="40" high="80" optimum="100"></meter>
(après avoir décoché « Verrouiller » et cliqué sur 👁)
le champ e-mail devient modifiable, son aide « nom@domaine.tn » apparaît,
le lien redevient accessible au clavier (tabindex="0"), le code s'affiche en clair (type="text")
  • [for] et [id] construits avec le même identifiant relient chaque étiquette à son champ : un clic sur « E-mail » place le curseur dans le champ.
  • [type] change la nature du champ à la volée : c'est la technique du bouton « afficher le mot de passe ».
  • [tabIndex]="-1" retire le lien du parcours au clavier tant que la fiche est verrouillée.
  • Le lien mailto: est calculé dans un computed: le template reste lisible et encodeURIComponent protège les espaces du sujet.
  1. La liaison d'attribut [attr.…]

    • Certains attributs n'ont aucune propriété correspondante : les attributs data-*, role, la plupart des attributs ARIA, et les attributs des éléments SVG. On les lie avec le préfixe attr..
    • La valeur est convertie en chaîne : [attr.aria-expanded]="false" écrit aria-expanded="false". Pour retirer un attribut, donnez-lui la valeur null (ou undefined).
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-attributs',
  template: `
    <table>
      <tr><th [attr.colspan]="colonnes">Résultats du groupe</th></tr>
      <tr><td>Amira</td><td>16</td><td [attr.data-mention]="'TB'">Très bien</td></tr>
    </table>

    <button [attr.aria-expanded]="ouvert()" [attr.aria-controls]="'details'" (click)="ouvert.set(!ouvert())">
      Détails
    </button>
    <p id="details" [attr.role]="'region'" [hidden]="!ouvert()">Séances le samedi matin.</p>

    <p [attr.title]="aide()" [attr.data-etat]="ouvert() ? 'ouvert' : null">Survolez-moi</p>
    <button (click)="aide.set(aide() ? null : 'Une aide contextuelle')">Aide : {{ aide() ? 'oui' : 'non' }}</button>

    <svg width="120" height="60">
      <circle cx="30" cy="30" [attr.r]="rayon()" fill="#DD0031" />
      <rect x="70" [attr.y]="60 - rayon() * 2" width="30" [attr.height]="rayon() * 2" fill="#3178C6" />
    </svg>
    <input type="range" min="5" max="30" [value]="rayon()" (input)="rayon.set(+$event.target.value)" />
  `,
  styles: `[data-mention='TB'] { color: #16a34a; font-weight: 700; } [data-etat='ouvert'] { background: #fef3c7; }`,
})
export class Attributs {
  protected readonly colonnes = 3;
  protected readonly ouvert = signal(false);
  protected readonly aide = signal<string | null>(null);
  protected readonly rayon = signal(20);
}
<th colspan="3">Résultats du groupe</th>
<td data-mention="TB">Très bien</td>             ← en vert grâce au sélecteur CSS [data-mention='TB']
<button aria-expanded="false" aria-controls="details"> Détails </button>
<p id="details" hidden="" role="region">Séances le samedi matin.</p>
<p>Survolez-moi</p>                               ← aucun attribut : les deux valeurs sont null
<circle cx="30" cy="30" fill="#DD0031" r="20"></circle>
(après Détails, Aide, et le curseur à 10)
<button aria-expanded="true" aria-controls="details"> Détails </button>
<p data-etat="ouvert" title="Une aide contextuelle">Survolez-moi</p>
<circle cx="30" cy="30" fill="#DD0031" r="10"></circle>
<rect x="70" width="30" fill="#3178C6" y="40" height="20"></rect>
  • Les attributs data-* se combinent très bien avec les sélecteurs d'attribut CSS ([data-mention='TB']) pour styler un élément selon son état.
  • Sans le préfixe, la compilation échoue : NG8002: Can't bind to 'data-etat' since it isn't a known property of 'p'., et pour le SVG NG8002: Can't bind to 'r' since it isn't a known property of ':svg:circle'.
  • Pour les attributs ARIA, Angular accepte aussi l'écriture sans préfixe ([aria-expanded]) ; la forme [attr.aria-expanded] reste la plus répandue dans les projets existants.
  • Attention : false ne retire pas un attribut : [attr.aria-current]="false" écrit aria-current="false". Seuls null et undefined le suppriment.
  1. Lier les entrées d'un composant

    • Une liaison de propriété peut viser l'entrée d'un composant enfant, déclarée avec input() (voir « Les décorateurs Angular »). C'est ainsi qu'un parent configure ses enfants.
    • Sans crochets, l'entrée reçoit un texte fixe ; avec transform: numberAttribute ou booleanAttribute, ce texte est converti en nombre ou en booléen, et un attribut sans valeur (enLigne) vaut true.

Le parent fournit les valeurs aux entrées input() de l'enfant

import { Component, booleanAttribute, computed, input, numberAttribute } from '@angular/core';

@Component({
  selector: 'app-badge-utilisateur',
  template: `
    <span class="pastille" [style.width.px]="taille()" [style.height.px]="taille()" [style.background]="couleur()"
          [class.en-ligne]="enLigne()" [title]="nom() + (enLigne() ? ' (en ligne)' : '')">
      {{ initiales() }}
    </span>
    {{ nom() }}
  `,
  styles: `
    :host { display: inline-flex; align-items: center; gap: 8px; margin-right: 18px; }
    .pastille { display: grid; place-items: center; border-radius: 50%; color: #fff; font-weight: 700; }
    .en-ligne { outline: 3px solid #16a34a; outline-offset: 2px; }
  `,
})
export class BadgeUtilisateur {
  readonly nom = input.required<string>();
  readonly taille = input(40, { transform: numberAttribute });
  readonly enLigne = input(false, { transform: booleanAttribute });
  readonly couleur = input('#6366f1');
  protected readonly initiales = computed(() =>
    this.nom().split(' ').map((mot) => mot[0]).join('').toUpperCase(),
  );
}
import { Component, signal } from '@angular/core';
import { BadgeUtilisateur } from './badge-utilisateur';

@Component({
  selector: 'app-equipe',
  imports: [BadgeUtilisateur],
  template: `
    <app-badge-utilisateur nom="Amira Ben Ali" />
    <app-badge-utilisateur nom="Sami Trabelsi" taille="56" enLigne couleur="#DD0031" />
    <app-badge-utilisateur [nom]="formateur()" [taille]="taille()" [enLigne]="connecte()" />
    <button (click)="connecte.set(!connecte())">Connexion</button>
    <button (click)="taille.update((t) => t + 8)">Agrandir</button>
  `,
})
export class Equipe {
  protected readonly formateur = signal('Karim Mansour');
  protected readonly taille = signal(32);
  protected readonly connecte = signal(false);
}
ABA Amira Ben Ali        ← 40 px, violet (valeurs par défaut)
ST  Sami Trabelsi        ← 56 px, rouge, cerclé de vert (en ligne)
KM  Karim Mansour        ← 32 px, violet
(après Connexion et Agrandir)
KM  Karim Mansour        ← 40 px, cerclé de vert, info-bulle « Karim Mansour (en ligne) »
  • Les entrées sont vérifiées à la compilation: oublier nom donne NG8008: Required input 'nom' from component BadgeUtilisateur must be specified.; écrire [couleur]="12" donne TS2322: Type 'number' is not assignable to type 'string'.; une entrée inexistante [age] donne NG8002: Can't bind to 'age' since it isn't a known property of 'app-badge-utilisateur'.
  • Le composant enfant doit figurer dans les imports du parent, sinon : NG8001: 'app-badge-utilisateur' is not a known element.
  • Un même composant affiche ainsi des données fixes (écrites dans le template) ou vivantes (des signals du parent) : c'est la base des composants réutilisables.
  1. Bonnes pratiques avec les signals

    • Lisez des signals dans vos liaisons : [src]="photo().url". Un signal oublié sans parenthèses ([value]="email") déclenche l'avertissement NG8109: email is a function and should be invoked: email().
    • Calculez dans la classe avec computed() plutôt que dans le template : [href]="lienCourriel()" est plus lisible et mémorise son résultat.
    • Pas d'effet de bord: une liaison de propriété lit des données, elle n'en modifie aucune. [disabled]="email().length = 0" est refusé : NG5002: Parser Error: Bindings cannot contain assignments.
    • Gardez les expressions courtes: au-delà d'une condition simple, créez un computed au nom explicite (complet(), niveau()).
// À éviter : logique dans le template
<button [disabled]="formation().inscrits >= formation().places || !utilisateur()?.connecte">S'inscrire</button>

// Préférer : un computed au nom explicite
protected readonly inscriptionImpossible = computed(
  () => this.formation().inscrits >= this.formation().places || !this.utilisateur()?.connecte,
);
<button [disabled]="inscriptionImpossible()">S'inscrire</button>
  1. La sécurité : le nettoyage automatique

    • Une valeur liée peut venir d'un visiteur (un commentaire, un lien saisi dans un profil). Pour empêcher l'exécution de code malveillant (attaque XSS), Angular nettoie (sanitize) automatiquement les valeurs liées aux propriétés sensibles : [href], [innerHTML], certains styles…

Angular nettoie les URL et le HTML dangereux

import { Component, inject } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';

@Component({
  selector: 'app-securite',
  template: `
    <a [href]="lienSaisi">Lien saisi par un visiteur</a>
    <a [href]="lienSur">Lien vers la documentation</a>
    <div [innerHTML]="commentaire"></div>
    <div [innerHTML]="annonceDeConfiance"></div>
  `,
})
export class Securite {
  protected readonly lienSaisi = 'javascript:alert("piratage")';
  protected readonly lienSur = 'https://angular.dev';
  protected readonly commentaire = '<em>Super cours !</em><img src="x" onerror="alert(1)">';

  // Contenu écrit par l'équipe, jamais par un visiteur : on l'autorise explicitement
  protected readonly annonceDeConfiance: SafeHtml = inject(DomSanitizer).bypassSecurityTrustHtml(
    '<p style="color: #DD0031">Inscriptions ouvertes jusqu’au <b>15 novembre</b>.</p>',
  );
}
<a href="unsafe:javascript:alert(&quot;piratage&quot;)">Lien saisi par un visiteur</a>
<a href="https://angular.dev">Lien vers la documentation</a>
<div><em>Super cours !</em><img src="x"></div>
<div><p style="color: #DD0031">Inscriptions ouvertes jusqu’au <b>15 novembre</b>.</p></div>

Console :
WARNING: sanitizing unsafe URL value javascript:alert("piratage")
WARNING: sanitizing HTML stripped some content
  • Le lien javascript: est neutralisé par le préfixe unsafe:: un clic ne fait plus rien. Le commentaire garde sa mise en forme (<em>), mais perd l'attribut onerror.
  • Sans bypassSecurityTrustHtml, l'attribut style de l'annonce serait lui aussi supprimé.
  • Attention : les méthodes bypassSecurityTrust… désactivent la protection. Ne les utilisez jamais sur une donnée saisie par un visiteur, seulement sur un contenu écrit par votre équipe.
  1. Aide-mémoire

Aide-mémoire du property binding Angular

  1. Activité 1 : une galerie de photos

    • Générer le composant act-property/galerie (quatre images photo-1.svg à photo-4.svg dans public/, avec une légende chacune) et un signal index. La galerie doit :
      • afficher la grande image avec [src] et [alt] calculés depuis un computed photo(), et la légende « Légende – 3 / 4 » ;
      • désactiver Précédente sur la première photo et Suivante sur la dernière ([disabled]) ;
      • afficher les quatre miniatures dans des boutons : la miniature active est encadrée ([class.active]) et porte aria-current="true", les autres aucun attribut aria-current;
      • donner à chaque bouton de miniature une info-bulle ([title]) avec la légende, et à chaque miniature un texte alternatif vide (image décorative).

Résultat attendu de l'activité 1 après un clic sur la troisième miniature

  • Écrire [attr.aria-current]="i === index()": que montre l'inspecteur sur les miniatures inactives ? Pourquoi la version avec null est-elle préférable ?
  • Pourquoi le texte alternatif des miniatures peut-il rester vide, alors que celui de la grande image est obligatoire ?
  1. Activité 2 : un champ de mot de passe sécurisé

    • Générer le composant act-property/mot-de-passe:
      • un champ dont le [type] bascule entre password et text grâce à un bouton Afficher / Masquer qui porte [attr.aria-pressed] et un [title] adapté ;
      • une longueur maximale de 20 caractères ([maxLength]) ;
      • quatre règles (8 caractères, une majuscule, un chiffre, un caractère spécial) calculées dans un computed et affichées en vert ou en rouge ([class.ok]) ;
      • une jauge <meter> liée par [value], [min], [max], [low], [high] et [optimum], suivie d'un niveau en toutes lettres (« Très faible » à « Excellent ») ;
      • [attr.aria-invalid] à true tant que moins de trois règles sont respectées, et un bouton Enregistrer désactivé dans ce cas.

Résultat attendu de l'activité 2 avec le mot de passe Angular22 affiché

  • Avec Angular22, le niveau est « Bon » et le bouton est actif ; en ajoutant !, il passe à « Excellent ». Coller un texte de 30 caractères : combien en reste-t-il ? Quelle liaison en est responsable ?
  • Pourquoi la couleur de la jauge change-t-elle sans aucune classe CSS ? Quel rôle jouent low, high et optimum?
  1. Activité 3 : un tableau de notes accessible

    • Générer le composant act-property/tableau-notes qui affiche les moyennes de quatre stagiaires (Amira 16,25 ; Sami 9,5 ; Ines 13,75 ; Youssef 11) :
      • la colonne Moyenne est triable par un bouton dans son en-tête : aucun tri → décroissant → croissant → aucun tri. L'en-tête porte [attr.aria-sort] (none, descending, ascending) et une flèche ↕ ▼ ▲ ;
      • chaque moyenne est arrondie à un chiffre, avec la valeur exacte en info-bulle ([title]) ;
      • la cellule Mention porte [attr.data-mention]; sa couleur est donnée uniquement par des sélecteurs CSS d'attribut ([data-mention='Très bien']…) ;
      • une ligne de pied « Moyenne du groupe » fusionne deux cellules avec [attr.colspan].

Résultat attendu de l'activité 3 trié par moyenne décroissante

  • Pourquoi le tri ne doit-il pas modifier le tableau d'origine ? Que se passerait-il si lignes() appelait directement this.stagiaires.sort(…)?
  • Remplacer [attr.colspan] par [colSpan], puis par [colspan]: lesquelles compilent ?
  1. Activité 4 : une carte produit réutilisable

    • Générer le composant act-property/carte-produit avec cinq entrées : titre (obligatoire), prix (obligatoire, numberAttribute), image ('formation.svg' par défaut), promo (booleanAttribute, −20 %) et stock (numberAttribute, 0 par défaut). La carte :
      • barre l'ancien prix en promotion et affiche le prix final en dinars (currency: 'TND') ;
      • est encadrée en orange en promotion ([class.promo]) et estompée en rupture de stock ([class.epuise]) ;
      • désactive son bouton (« Épuisé ») quand le stock est nul, avec une info-bulle « Rupture de stock » ou « N en stock ».
    • L'utiliser trois fois dans un composant vitrine: deux cartes avec des valeurs fixes (dont une avec l'attribut promo sans valeur) et une avec des valeurs liées à un objet vedette de la classe.
<app-carte-produit titre="Formation Angular" prix="450" stock="5" promo />
<app-carte-produit titre="Formation TypeScript" prix="300" />
<app-carte-produit [titre]="vedette.titre" [prix]="vedette.prix" [image]="vedette.image" [stock]="vedette.stock" />

Résultat attendu de l'activité 4 : trois cartes produit

  • Supprimer prix="300" de la deuxième carte : quelle erreur obtenez-vous ? Écrire prix="gratuit": la compilation réussit-elle ? Qu'affiche la carte, et pourquoi ?
  • Remplacer [stock]="vedette.stock" par stock="vedette.stock": expliquer le résultat.
  1. Activité 5 : un histogramme en SVG

    • Générer le composant act-property/histogramme qui dessine les inscriptions de cinq mois (18, 32, 25, 12, 28) dans un <svg> de 400 × 200 :
      • une barre <rect> par mois, dont x, y, width et height sont calculés par [attr.…] à partir d'une echelle() (la plus grande valeur occupe toute la hauteur utile) ;
      • la ou les barres maximales en rouge, les autres en bleu ([attr.fill]) ;
      • la valeur au-dessus de chaque barre et le nom du mois en dessous (<text> positionnés par [attr.x] et [attr.y]) ;
      • un [attr.aria-label] qui décrit toutes les valeurs pour les lecteurs d'écran, et une liste déroulante avec deux boutons +5 / −5 pour modifier un mois.

Résultat attendu de l'activité 5 après avoir ajouté 20 inscriptions en décembre

  • Ajouter 20 inscriptions en décembre : pourquoi deux barres sont-elles rouges ? Continuer à augmenter décembre : que deviennent les autres barres, et pourquoi ?
  • Remplacer [attr.height] par [height] sur le rectangle : quelle erreur obtenez-vous ?
  • Faire un commit après chaque activité, puis un push : git commit -m "Property binding : activité N".
  1. Erreurs fréquentes

Message ou symptôme Cause Solution
NG8002: Can't bind to 'scr' since it isn't a known property of 'img'. Faute de frappe dans le nom de la propriété Corriger : [src]
NG8002: Can't bind to 'data-etat' since it isn't a known property of 'p'. Attribut sans propriété correspondante [attr.data-etat]
NG8002: Can't bind to 'r' since it isn't a known property of ':svg:circle'. Attribut SVG lié comme une propriété [attr.r], [attr.height]…
NG8002: Can't bind to 'age' since it isn't a known property of 'app-badge-utilisateur'. Le composant n'a pas d'entrée de ce nom Déclarer age = input(…) ou corriger le nom
NG8001: 'app-badge-utilisateur' is not a known element Composant enfant absent des imports Ajouter le composant dans imports
NG8008: Required input 'nom' from component BadgeUtilisateur must be specified. Entrée obligatoire non fournie Ajouter nom="…" ou [nom]="…"
TS2322: Type 'number' is not assignable to type 'string'. Valeur du mauvais type passée à une entrée Passer le bon type, ou ajouter un transform
NG5002: Parser Error: Bindings cannot contain assignments Affectation dans une liaison de propriété Modifier les données dans une méthode ou un événement
NG8109: email is a function and should be invoked: email() (avertissement) Signal lié sans parenthèses [value]="email()"
Bouton désactivé avec disabled="{{ false }}" L'interpolation transmet la chaîne "false" [disabled]="false"
Le mot « logo » s'affiche au lieu de l'image (src="logo") Crochets oubliés : texte fixe [src]="logo"
aria-current="false" présent sur tous les éléments false ne retire pas un attribut Donner la valeur null
href="unsafe:javascript:…" et WARNING: sanitizing unsafe URL value URL dangereuse neutralisée par Angular Normal : ne jamais lier une URL javascript:
WARNING: sanitizing HTML stripped some content [innerHTML] contenait des scripts ou des attributs dangereux Normal ; bypassSecurityTrustHtml seulement pour un contenu de confiance
  1. À retenir

    • [cible]="expression" copie une donnée de la classe vers le template, à sens unique, et suit automatiquement les signals.
    • Les crochets conservent le type de la valeur ; l'interpolation produit une chaîne; sans les deux, c'est un texte fixe. Booléens et nombres : toujours entre crochets.
    • La cible est une propriété du DOM ([src], [disabled], [type]…), une entrée de composant, un attribut ([attr.…] pour data-*, role, ARIA, SVG) ou une classe / un style.
    • null retire un attribut ; false l'écrit sous forme de texte.
    • Les entrées de composant sont vérifiées à la compilation : nom, présence et type.
    • Angular nettoie automatiquement les URL et le HTML dangereux ; ne contournez cette protection que pour un contenu de confiance.
Des crochets, une cible, une expression : la liaison de propriété est la façon dont la classe pilote l'affichage. Liez des signals, calculez dans la classe, et laissez Angular s'occuper du reste.