Afficher une chaîne contenant des balises HTML

Comment afficher à l’écran une chaîne contenant du HTML de sorte que les balises HTML soient interprétées, et non affichées littéralement à l’écran ?

Impossible d’afficher des balises HTML avec l’interpolation

Si vous utilisez Angular pour afficher des contenus de type CMS (articles, billets de blog…) venant d’une base de données, vous serez confronté à la problématique des balises HTML.

En effet, les contenus CMS sont souvent un mélange de texte et de balises HTML. Mais si vous essayez d’interpoler une propriété contenant à la fois du texte et des balises HTML, les balises seront échappées et apparaîtront telles quelles à l’écran au lieu d’être interprétées.

Ainsi le composant suivant :

@Component({
  template: '{{ contenuHTML }}'
})
export class MyComponent {
  // Dans un vrai projet, ce contenu viendrait de la base de données.
  contenuHTML = '<p>Je suis du contenu <em>dynamique</em>.</p>';
}

Produit l’affichage ci-dessous :

<p>Je suis du contenu <em>dynamique</em>.</p>

Seul le HTML placé directement dans le template d’un composant est interprété tel quel.

Au cas où vous seriez tenté de créer un composant pour chaque contenu du site pour contourner ce problème, gardez en tête toutes les difficultés que vous devriez affronter : le contenu serait statique (au lieu de venir d’une base de données), pas de séparation entre le contenu et le formatage, maintenabilité horrible, performances horribles (imaginez si tous les composants de contenu sont chargés à l’affichage initial du site)… Ce n’est donc pas envisageable, à moins que votre site ne fasse que quelques dizaines de pages.

Il faut donc trouver un moyen d’afficher dans un composant Angular du HTML obtenu dynamiquement.

Binder à la propriété innerHTML

La solution est de binder la variable contenant le HTML à la propriété DOM innerHTML d’une balise, avec la syntaxe du binding de propriété :

@Component({
  template: '<div [innerHTML]="contenuHTML"></div>'
})
export class MyComponent {
  // Dans un vrai projet, ce contenu viendrait de la base de données.
  contenuHTML = '<p>Je suis du contenu <em>dynamique</em>.</p>';
}

Cette fois-ci le HTML est interprété, et on voit à l’écran :

Je suis du contenu dynamique.

Limitations

Le HTML est “nettoyé” par Angular

Le HTML qu’on binde à [innerHTML] est “nettoyé” par Angular pour des raisons de sécurité. Par exemple, les balises <script> ou les attributs style="" sont supprimés.

Si c’est un problème pour vous, vous pouvez toujours signaler à Angular que la chaîne bindée est “digne de confiance” (et qu’il ne doit pas la toucher) avec une méthode comme DomSanitizer#bypassSecurityTrustHtml. Mais attention aux failles de sécurité !

Une balise bidon doit parfois être utilisée

Pour porter le binding à [innerHTML], on doit parfois introduire dans le template une balise “bidon”, qui n’aurait pas forcément été là s’il n’y avait pas eu le binding. Dans notre exemple, c’est <div>...</div>.

Ce n’est pas un gros problème, mais c’est une forme de pollution. Et gardez en tête que certaines balises impacteront l’affichage final (une <div> génère un nouveau bloc, par exemple).

Les syntaxes Angular ne sont pas supportées

Si notre chaîne contient des syntaxes Angular (comme *ngIf, *ngFor…) en plus du texte et du HTML, ces syntaxes ne seront pas supportées/interprétées. C’est logique, car on se contente d’afficher une chaîne.

Pour supporter les syntaxes Angular, il faudrait que cette chaîne soit compilée comme un template de composant. Si c’est ce que vous recherchez, consultez la recette #66 Créer un composant dynamiquement.

Informations

Tags : templates

Dernière mise à jour :

Auteur : AngularChef

Qualité : Pas finalisé

Recettes liées :