~2 min de lecture
Angular : Supprime Les Div Inutiles, Utilise Le Host Élément
Tu wraps tous tes composants dans une <div> ? Ton DOM est rempli de containers inutiles ?
Il existe une meilleure façon : utiliser l'élément host du composant directement.
Dans cet article, je vais te montrer comment éliminer les div superflues et styler le host élément.
Le Problème Avec Les Div Wrappers
❌ Ce que font les juniors :
Ils ajoutent une div wrapper dans chaque template.
@Component({
selector: 'app-user-card',
template: `
<!-- ❌ Div wrapper inutile -->
<div class="user-card">
<h3>{{ user().name }}</h3>
<p>{{ user().email }}</p>
</div>
`,
styles: `
.user-card {
padding: 16px;
border: 1px solid #ddd;
border-radius: 8px;
}
`
})
export class UserCard {
readonly user = input.required<User>();
}
Résultat dans le DOM :
<app-user-card>
<div class="user-card"> <!-- ❌ Div inutile -->
<h3>John</h3>
<p>[john@example.com](mailto:john@example.com)</p>
</div>
</app-user-card>
🚨 Pourquoi c'est problématique ?
- DOM pollué : élément HTML inutile
- Styles complexes : un niveau de nesting de plus
- Flexbox/Grid : complique les layouts
- Performance : plus d'éléments = plus lent
- Lisibilité : code moins clair
La Solution Pro : Utiliser Le Host
✅ Ce que font les pros :
Ils stylent directement l'élément host.
@Component({
selector: 'app-user-card',
standalone: true,
template: `
<!-- ✅ Pas de div wrapper -->
<h3>{{ user().name }}</h3>
<p>{{ user().email }}</p>
`,
styles: `
/* ✅ Style sur le host directement */
:host {
display: block;
padding: 16px;
border: 1px solid #ddd;
border-radius: 8px;
}
`
})
export class UserCard {
readonly user = input.required<User>();
}
Résultat dans le DOM :
<app-user-card style="...">
<!-- ✅ Pas de div inutile -->
<h3>John</h3>
<p>[john@example.com](mailto:john@example.com)</p>
</app-user-card>
💡 Pourquoi c'est mieux ?
- DOM plus propre : moins d'éléments
- Styles simplifiés : moins de nesting
- Layouts faciles : Flexbox/Grid direct
- Performance : moins de nodes
- Plus maintenable : code plus clair
Le Sélecteur :host
Syntaxe de base
styles: `
/* Style par défaut du host */
:host {
display: block;
padding: 16px;
}
/* Host avec classe */
:host(.highlighted) {
background: yellow;
}
/* Host avec attribut */
:host([disabled]) {
opacity: 0.5;
}
`
Display du host
// ✅ Toujours définir le display
styles: `
:host {
display: block; // ou flex, grid, inline-block...
}
`
Pourquoi ? Par défaut, le host est display: inline, ce qui peut causer des bugs de layout.
Cas d'Usage Réels
Exemple 1 : Card Component
@Component({
selector: 'app-card',
template: `
<h3><ng-content select="[header]" /></h3>
<div class="content">
<ng-content />
</div>
`,
styles: `
:host {
display: block;
background: white;
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
:host(:hover) {
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
`
})
export class Card {
}
Exemple 2 : Button Component
@Component({
selector: 'app-button',
host: {
'[class.primary]': 'variant() === "primary"',
'[class.secondary]': 'variant() === "secondary"',
'[class.disabled]': 'disabled()'
},
template: `<ng-content />`,
styles: `
:host {
display: inline-block;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
border: none;
font-size: 14px;
}
:host(.primary) {
background: #007bff;
color: white;
}
:host(.secondary) {
background: #6c757d;
color: white;
}
:host(.disabled) {
opacity: 0.5;
cursor: not-allowed;
}
`
})
export class Button {
readonly variant = input<'primary' | 'secondary'>('primary');
readonly disabled = input<boolean>(false);
}
Exemple 3 : Layout avec Flexbox
@Component({
selector: 'app-row',
template: `<ng-content />`,
styles: `
:host {
display: flex;
gap: 16px;
align-items: center;
}
`
})
export class Row {
}
@Component({
selector: 'app-column',
template: `<ng-content />`,
styles: `
:host {
display: flex;
flex-direction: column;
gap: 16px;
}
`
})
export class Column {
}
// Utilisation
@Component({
template: `
<app-row>
<app-column>
<app-card>Card 1</app-card>
<app-card>Card 2</app-card>
</app-column>
<app-column>
<app-card>Card 3</app-card>
</app-column>
</app-row>
`
})
export class Dashboard {
}
Exception : Les Formulaires
⚠️ Cas où la div wrapper est nécessaire
Pour les formulaires avec <form>, il faut garder la balise <form> dans le template.
// ✅ Bon : <form> dans le template
@Component({
selector: 'app-user-form',
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<input formControlName="name" />
<button type="submit">Save</button>
</form>
`,
styles: `
:host {
display: block;
}
form {
padding: 16px;
}
`
})
export class UserForm {
readonly form = inject(FormBuilder).group({
name: ['']
});
onSubmit(): void {
console.log(this.form.value);
}
}
Pourquoi garder <form> ?
- Sémantique HTML :
<form>a une signification - Validation HTML5 : formulaires natifs
- Accessibilité : lecteurs d'écran
- Submit natif : Enter key, etc.
Host Bindings
Avec la propriété host
@Component({
selector: 'app-badge',
standalone: true,
host: {
'[class.success]': 'type() === "success"',
'[class.error]': 'type() === "error"',
'[attr.role]': '"status"',
'[style.backgroundColor]': 'backgroundColor()'
},
template: `<ng-content />`,
styles: `
:host {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
}
:host(.success) { background: #28a745; color: white; }
:host(.error) { background: #dc3545; color: white; }
`
})
export class Badge {
readonly type = input<'success' | 'error'>('success');
readonly backgroundColor = input<string>();
}
Avec @HostBinding (ancien style)
@Component({
selector: 'app-card',
template: `<ng-content />`
})
export class Card {
@HostBinding('class.elevated')
readonly elevated = input<boolean>(false);
@HostBinding('style.padding.px')
readonly padding = input<number>(16);
}
Les Pièges À Éviter
Oublier le display
// ❌ Mauvais : pas de display
styles: `
:host {
padding: 16px;
}
`
// ✅ Bon : display défini
styles: `
:host {
display: block;
padding: 16px;
}
`
Conclusion : DOM Plus Propre
Utiliser le host élément rend ton code plus propre et performant.
Ce que tu dois retenir :
✅ :host pour styler le composant
✅ display toujours défini
✅ Pas de div wrapper sauf exceptions
✅ Exceptions : formulaires, animations, layouts complexes
✅ host bindings pour classes dynamiques
La règle d'or :
Supprime la div wrapper. Si tu as vraiment besoin d'un container, demande-toi pourquoi. Dans 80% des cas, :host suffit.
Envie de découvrir EasyAngularKit et son programme ?
→ Découvre EasyAngularKit : https://pim.ms/C31g7p2