Patterns Angular
Comment créer un composant qui consomme Ori proprement.
Composer plutôt qu’étendre
Les composants Ori exposent des classes CSS sémantiques (ori-input,
ori-card, etc.) plus une API Angular standalone. La règle :
- Pour un cas standard : utiliser le composant tel quel
- Pour un variant qui revient souvent : créer un composant applicatif qui pré-configure
- Pour quelque chose de très différent : composer un nouveau composant à partir des classes CSS du DS
Exemple : wrapper d’un Input avec compteur de caractères
import { Component, Input } from '@angular/core';
import { OriInputComponent } from '@govpf/ori-angular';
@Component({
selector: 'app-input-with-counter',
standalone: true,
imports: [OriInputComponent],
template: `
<ori-input
[label]="label"
[(value)]="value"
[hint]="value.length + ' / ' + maxLength + ' caractères'"
[maxLength]="maxLength"
></ori-input>
`,
})
export class InputWithCounterComponent {
@Input() label = '';
@Input() maxLength = 280;
value = '';
}
Pas de fork du composant Input - juste un composant applicatif qui ajoute du comportement.
Réutiliser les classes CSS pour un composant 100% custom
Pour un composant qui n’existe pas dans Ori mais qui doit avoir le look du DS, réutiliser directement les classes CSS :
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-result-card',
standalone: true,
template: `
<article class="ori-card ori-card--elevated" style="padding: 1.25rem;">
<h3 class="ori-card__title">{{ title }}</h3>
<p style="font-size: 2rem; font-weight: 600; margin: 0.5rem 0 0;">{{ count }}</p>
</article>
`,
})
export class ResultCardComponent {
@Input() title = '';
@Input() count = 0;
}
Le DS expose une trentaine de classes ori-* documentées dans
Fondations / Tokens / Sémantique.
Forms réactifs avec ReactiveFormsModule
Les composants form Ori n’implémentent pas ControlValueAccessor
nativement (volontaire pour rester découplés). Pour les utiliser avec
[formControl], wrapper :
import { Component, Input } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { OriInputComponent } from '@govpf/ori-angular';
@Component({
selector: 'app-rf-input',
standalone: true,
imports: [OriInputComponent, ReactiveFormsModule],
template: `
<ori-input
[label]="label"
[value]="control.value || ''"
(valueChange)="control.setValue($event)"
[error]="control.touched && control.invalid ? errorMessage : ''"
(blur)="control.markAsTouched()"
></ori-input>
`,
})
export class RfInputComponent {
@Input({ required: true }) control!: FormControl<string | null>;
@Input() label = '';
@Input() errorMessage = 'Champ invalide';
}
Pattern similaire pour <ori-checkbox>, <ori-select>, etc. À factoriser
en cas d’usages multiples.
Détection de changement OnPush
Tous les composants Ori utilisent ChangeDetectionStrategy.OnPush.
Cela signifie qu’ils ne se rafraîchissent que sur :
- Changement d’
@Input()(par référence pour les objets/tableaux) - Émission d’un
@Output() - Appel manuel à
markForCheck()/detectChanges()
Muter un objet en place (this.user.name = 'X') puis le passer à
un composant Ori ne déclenche pas le rendu. Solution : remplacer la
référence (this.user = { ...this.user, name: 'X' }).
Anti-patterns
- ❌ Surcharger une classe
ori-*dans le CSS global pour la modifier. Ça casse la cohérence DS et le dark mode. - ❌ Utiliser
<button class="ori-btn ori-btn--primary">à la main au lieu de<ori-button variant="primary">. Tu perds le typage et l’a11y. - ❌ Importer plusieurs
BrowserModuleou autreModuleglobal - les composants Ori sont standalone et n’en demandent pas.