Intégration web angularforall.com

- PrimeNG : bibliothèque UI pour Angular

Primeng Angular Ui-Components Bootstrap Typescript
PrimeNG : bibliothèque UI pour Angular

Guide complet PrimeNG : installation, composants, thèmes et intégration dans vos projets Angular 17+.

Pourquoi choisir PrimeNG ?

Lorsqu'on développe une application Angular professionnelle — tableau de bord, ERP, back-office, ou CRM — le besoin de composants UI riches et fiables se fait rapidement sentir. Créer de toutes pièces une table avec pagination, tri multi-colonnes et filtres personnalisés représente plusieurs jours de travail. PrimeNG résout ce problème en fournissant plus de 90 composants prêts à l'emploi, maintenus activement par PrimeTek et utilisés par des milliers d'équipes dans le monde.

PrimeNG est bien plus qu'une simple collection de boutons et de champs de formulaire. La bibliothèque inclut des composants complexes comme p-table (DataTable avec virtual scroll, lazy loading et édition inline), p-treeTable pour les données hiérarchiques, p-fileUpload avec preview drag-and-drop, et une intégration native de Chart.js via p-chart. Tous ces composants partagent un système de thème cohérent basé sur des variables CSS, ce qui garantit une interface homogène sans effort.

Depuis la version 17, PrimeNG adopte pleinement le modèle standalone components d'Angular. Chaque composant s'importe individuellement, ce qui facilite le tree-shaking et réduit la taille du bundle final. Ce guide vous accompagne de l'installation jusqu'aux patterns avancés, avec des exemples concrets, commentés et directement applicables dans vos projets Angular 17+.

Ce que PrimeNG apporte concrètement

  • Plus de 90 composants UI : tables, formulaires, menus, charts, overlays, media…
  • Système de thèmes CSS basé sur des Design Tokens — dark mode inclus nativement
  • Accessibilité WCAG 2.1 intégrée dans chaque composant (ARIA, navigation clavier)
  • PrimeIcons : bibliothèque d'icônes incluse (300+ icônes vectorielles)
  • Compatible Angular 15, 16, 17, 18, 19 avec support standalone
  • PrimeFlex : utilitaires CSS optionnels (équivalent Tailwind/Bootstrap utilities)
Version recommandee : Utilisez PrimeNG 17+ pour les projets Angular 17+. La version 17 est une refonte majeure avec un nouveau moteur de thèmes (Design Tokens) et un support natif des composants standalone.

Installation et configuration

L'installation de PrimeNG se fait en quelques commandes. La configuration differe selon que vous utilisez des modules Angular classiques ou les composants standalone (recommande pour Angular 17+).

Installation via npm

# Installer PrimeNG et PrimeIcons (icones incluses)
npm install primeng primeicons

# Optionnel : PrimeFlex (utilitaires CSS)
npm install primeflex

Ajouter le theme dans angular.json

PrimeNG propose plusieurs themes officiels. Ajoutez-en un dans le tableau styles de votre angular.json. Le theme Aura est recommande pour les nouveaux projets.

// angular.json — dans "projects > votre-app > architect > build > options"
{
  "styles": [
    // Theme PrimeNG — choisir un seul theme
    "node_modules/primeng/resources/themes/aura-light-blue/theme.css",
    // Icones PrimeNG
    "node_modules/primeicons/primeicons.css",
    // Votre feuille de style globale
    "src/styles.css"
  ]
}

Configuration dans app.config.ts (Angular 17+ standalone)

// src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { providePrimeNG } from 'primeng/config';
import Aura from '@primeng/themes/aura';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),

    // Active les animations Angular (obligatoire pour PrimeNG)
    provideAnimationsAsync(),

    // Configure PrimeNG avec le theme Aura
    providePrimeNG({
      theme: {
        preset: Aura,
        options: {
          // Active le dark mode automatique selon prefers-color-scheme
          darkModeSelector: 'system',
        },
      },
    }),
  ],
};

Configuration dans app.module.ts (Angular classique avec NgModule)

// src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

// Importer uniquement les modules des composants utilises
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
import { TableModule } from 'primeng/table';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule, // Obligatoire pour les animations PrimeNG
    ButtonModule,
    InputTextModule,
    TableModule,
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}
BrowserAnimationsModule est obligatoire pour que les composants PrimeNG avec animations (Dialog, Toast, Dropdown, etc.) fonctionnent correctement. En standalone, utilisez provideAnimationsAsync() dans app.config.ts.

Verification de l'installation

// src/app/app.component.ts — test minimal
import { Component } from '@angular/core';
import { ButtonModule } from 'primeng/button';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ButtonModule], // Import direct du composant PrimeNG
  template: `
    <!-- Si ce bouton s'affiche avec le style PrimeNG, l'installation est OK -->
    <p-button label="PrimeNG installe avec succes !" icon="pi pi-check" />
  `,
})
export class AppComponent {}

Composants essentiels

Voici les composants les plus utilises au quotidien, avec leurs configurations les plus courantes et des exemples complets commentes.

Button et Badge

Le composant p-button est le plus simple a prendre en main. Il supporte les icones PrimeIcons, les variantes de style, les etats de chargement et les tailles.

// boutons.component.ts
import { Component } from '@angular/core';
import { ButtonModule } from 'primeng/button';
import { BadgeModule } from 'primeng/badge';

@Component({
  selector: 'app-boutons',
  standalone: true,
  imports: [ButtonModule, BadgeModule],
  template: `
    <!-- Bouton standard avec icone -->
    <p-button label="Enregistrer" icon="pi pi-save" />

    <!-- Variante outlined -->
    <p-button label="Annuler" icon="pi pi-times" variant="outlined" severity="secondary" />

    <!-- Bouton danger -->
    <p-button label="Supprimer" icon="pi pi-trash" severity="danger" />

    <!-- Etat de chargement (desactive le clic automatiquement) -->
    <p-button label="Traitement..." [loading]="isLoading" (onClick)="submit()" />

    <!-- Badge sur un bouton (notifications) -->
    <p-button
      icon="pi pi-bell"
      [rounded]="true"
      [text]="true"
      badgeClass="p-badge-danger"
      badge="3"
    />
  `,
})
export class BoutonsComponent {
  isLoading = false;

  submit(): void {
    // Active le spinner de chargement pendant 2 secondes
    this.isLoading = true;
    setTimeout(() => this.isLoading = false, 2000);
  }
}

InputText et formulaires reactifs

PrimeNG s'integre parfaitement avec ReactiveFormsModule. Les composants acceptent formControlName et formControl directement.

// login-form.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { InputTextModule } from 'primeng/inputtext';
import { PasswordModule } from 'primeng/password';
import { ButtonModule } from 'primeng/button';
import { MessageModule } from 'primeng/message';

@Component({
  selector: 'app-login-form',
  standalone: true,
  imports: [ReactiveFormsModule, InputTextModule, PasswordModule, ButtonModule, MessageModule],
  template: `
    <form [formGroup]="loginForm" (ngSubmit)="onSubmit()" class="p-fluid">

      <!-- Champ email avec validation et message d'erreur -->
      <div class="field mb-3">
        <label for="email" class="block mb-1">Adresse email</label>
        <input
          pInputText
          id="email"
          type="email"
          formControlName="email"
          placeholder="votre@email.com"
          [class.ng-invalid]="email?.invalid && email?.touched"
          [class.ng-dirty]="email?.touched"
        />
        <!-- Message d'erreur conditionnel -->
        @if (email?.invalid && email?.touched) {
          <p-message severity="error" text="Email invalide" />
        }
      </div>

      <!-- Champ mot de passe avec jauge de force -->
      <div class="field mb-3">
        <label for="password" class="block mb-1">Mot de passe</label>
        <p-password
          formControlName="password"
          [toggleMask]="true"
          [feedback]="true"
          placeholder="Min. 8 caracteres"
        />
      </div>

      <p-button
        type="submit"
        label="Se connecter"
        icon="pi pi-sign-in"
        [disabled]="loginForm.invalid"
      />
    </form>
  `,
})
export class LoginFormComponent {
  loginForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.loginForm = this.fb.group({
      // Validation email obligatoire + format valide
      email:    ['', [Validators.required, Validators.email]],
      // Validation mot de passe obligatoire + longueur minimum
      password: ['', [Validators.required, Validators.minLength(8)]],
    });
  }

  // Getter pour acceder facilement au champ email dans le template
  get email() { return this.loginForm.get('email'); }

  onSubmit(): void {
    if (this.loginForm.valid) {
      console.log('Formulaire valide :', this.loginForm.value);
    }
  }
}

Table (p-table) avec pagination, tri et filtres

p-table est le composant phare de PrimeNG. Il gere la pagination, le tri multi-colonnes, les filtres globaux et par colonne, le lazy loading et l'edition inline.

// users-table.component.ts
import { Component, OnInit, signal } from '@angular/core';
import { TableModule } from 'primeng/table';
import { InputTextModule } from 'primeng/inputtext';
import { ButtonModule } from 'primeng/button';
import { TagModule } from 'primeng/tag';

export interface User {
  id: number;
  nom: string;
  email: string;
  role: 'admin' | 'editor' | 'viewer';
  actif: boolean;
}

@Component({
  selector: 'app-users-table',
  standalone: true,
  imports: [TableModule, InputTextModule, ButtonModule, TagModule],
  template: `
    <!-- Barre de recherche globale -->
    <div class="flex justify-content-between mb-3">
      <input
        pInputText
        type="text"
        placeholder="Rechercher un utilisateur..."
        (input)="dt.filterGlobal($any($event.target).value, 'contains')"
      />
    </div>

    <!-- Table avec pagination, tri et filtre global -->
    <p-table
      #dt
      [value]="users()"
      [paginator]="true"
      [rows]="10"
      [rowsPerPageOptions]="[5, 10, 25]"
      [sortMode]="'multiple'"
      [globalFilterFields]="['nom', 'email', 'role']"
      [tableStyle]="{ 'min-width': '60rem' }"
    >
      <ng-template pTemplate="header">
        <tr>
          <!-- pSortableColumn active le tri sur cette colonne -->
          <th pSortableColumn="nom">Nom <p-sortIcon field="nom" /></th>
          <th pSortableColumn="email">Email <p-sortIcon field="email" /></th>
          <th pSortableColumn="role">Role <p-sortIcon field="role" /></th>
          <th>Statut</th>
          <th>Actions</th>
        </tr>
      </ng-template>

      <ng-template pTemplate="body" let-user>
        <tr>
          <td>{{ user.nom }}</td>
          <td>{{ user.email }}</td>
          <td>
            <!-- p-tag colore automatiquement selon la severite -->
            <p-tag
              [value]="user.role"
              [severity]="getRoleSeverity(user.role)"
            />
          </td>
          <td>
            <p-tag
              [value]="user.actif ? 'Actif' : 'Inactif'"
              [severity]="user.actif ? 'success' : 'danger'"
            />
          </td>
          <td>
            <p-button icon="pi pi-pencil" [text]="true" severity="info" (onClick)="edit(user)" />
            <p-button icon="pi pi-trash" [text]="true" severity="danger" (onClick)="delete(user)" />
          </td>
        </tr>
      </ng-template>

      <!-- Message quand aucun resultat -->
      <ng-template pTemplate="emptymessage">
        <tr><td colspan="5" class="text-center p-4">Aucun utilisateur trouve.</td></tr>
      </ng-template>
    </p-table>
  `,
})
export class UsersTableComponent implements OnInit {
  // Signal Angular pour la liste des utilisateurs
  users = signal<User[]>([]);

  ngOnInit(): void {
    // Simulation de donnees (remplacer par un appel HTTP)
    this.users.set([
      { id: 1, nom: 'Alice Martin', email: 'alice@exemple.fr', role: 'admin', actif: true },
      { id: 2, nom: 'Bob Dupont', email: 'bob@exemple.fr', role: 'editor', actif: true },
      { id: 3, nom: 'Claire Leblanc', email: 'claire@exemple.fr', role: 'viewer', actif: false },
    ]);
  }

  // Retourne la couleur du badge selon le role
  getRoleSeverity(role: string): 'success' | 'info' | 'warning' {
    const map: Record<string, 'success' | 'info' | 'warning'> = {
      admin: 'warning',
      editor: 'info',
      viewer: 'success',
    };
    return map[role] ?? 'info';
  }

  edit(user: User): void { console.log('Editer', user); }
  delete(user: User): void { console.log('Supprimer', user); }
}

Dialog et ConfirmDialog

// user-actions.component.ts
import { Component } from '@angular/core';
import { DialogModule } from 'primeng/dialog';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { ButtonModule } from 'primeng/button';
import { ConfirmationService } from 'primeng/api';

@Component({
  selector: 'app-user-actions',
  standalone: true,
  imports: [DialogModule, ConfirmDialogModule, ButtonModule],
  // ConfirmationService doit etre fourni dans providers
  providers: [ConfirmationService],
  template: `
    <!-- Bouton qui ouvre le dialog -->
    <p-button label="Modifier l'utilisateur" icon="pi pi-pencil" (onClick)="dialogVisible = true" />

    <!-- Bouton de suppression avec confirmation -->
    <p-button label="Supprimer" severity="danger" (onClick)="confirmerSuppression()" />

    <!-- p-dialog : fenetre modale personnalisee -->
    <p-dialog
      header="Modifier l'utilisateur"
      [(visible)]="dialogVisible"
      [modal]="true"
      [style]="{ width: '450px' }"
      [draggable]="false"
    >
      <p>Contenu du formulaire d'edition ici...</p>
      <ng-template pTemplate="footer">
        <p-button label="Annuler" icon="pi pi-times" variant="outlined" (onClick)="dialogVisible = false" />
        <p-button label="Enregistrer" icon="pi pi-check" (onClick)="sauvegarder()" />
      </ng-template>
    </p-dialog>

    <!-- p-confirmDialog : dialogue de confirmation global -->
    <p-confirmDialog />
  `,
})
export class UserActionsComponent {
  dialogVisible = false;

  constructor(private confirmationService: ConfirmationService) {}

  confirmerSuppression(): void {
    this.confirmationService.confirm({
      message: 'Voulez-vous vraiment supprimer cet utilisateur ?',
      header: 'Confirmation de suppression',
      icon: 'pi pi-exclamation-triangle',
      // Callback si l'utilisateur confirme
      accept: () => console.log('Supprime !'),
      // Callback si l'utilisateur annule
      reject: () => console.log('Annule'),
    });
  }

  sauvegarder(): void {
    this.dialogVisible = false;
    console.log('Modifications enregistrees');
  }
}

Toast et MessageService

// notifications.component.ts
import { Component } from '@angular/core';
import { ToastModule } from 'primeng/toast';
import { ButtonModule } from 'primeng/button';
import { MessageService } from 'primeng/api';

@Component({
  selector: 'app-notifications',
  standalone: true,
  imports: [ToastModule, ButtonModule],
  providers: [MessageService], // MessageService injecte dans le composant
  template: `
    <!-- Placement du toast en haut a droite -->
    <p-toast position="top-right" />

    <div class="flex gap-2">
      <p-button label="Succes" severity="success" (onClick)="showSuccess()" />
      <p-button label="Erreur" severity="danger" (onClick)="showError()" />
      <p-button label="Info" severity="info" (onClick)="showInfo()" />
    </div>
  `,
})
export class NotificationsComponent {
  constructor(private messageService: MessageService) {}

  showSuccess(): void {
    this.messageService.add({
      severity: 'success',   // Couleur verte
      summary: 'Succes',
      detail: 'L\'enregistrement a bien ete effectue.',
      life: 3000,            // Disparait apres 3 secondes
    });
  }

  showError(): void {
    this.messageService.add({
      severity: 'error',
      summary: 'Erreur',
      detail: 'Une erreur est survenue. Veuillez reessayer.',
      sticky: true,          // Ne disparait pas automatiquement
    });
  }

  showInfo(): void {
    this.messageService.add({
      severity: 'info',
      summary: 'Information',
      detail: 'Votre session expire dans 5 minutes.',
    });
  }
}

Themes et personnalisation

PrimeNG 17+ introduit un nouveau systeme de themes base sur les Design Tokens. Chaque composant expose des variables CSS que vous pouvez surcharger globalement ou par composant, sans toucher au code source de la librairie.

Themes officiels disponibles

Theme Style visuel Variants Usage recommande
Aura Moderne, arrondi, Material-inspired light/dark × 14 couleurs Applications SaaS, dashboards
Lara Sobre, professionnel, bords droits light/dark × 14 couleurs ERP, back-office, entreprise
Nora Minimaliste, flat design light/dark × 14 couleurs Applications B2B, outils internes
Wind Inspire de Tailwind CSS light/dark Projets avec Tailwind CSS

Personnalisation via Design Tokens

// app.config.ts — personnalisation du theme Aura
import { ApplicationConfig } from '@angular/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { providePrimeNG } from 'primeng/config';
import Aura from '@primeng/themes/aura';
import { definePreset } from '@primeng/themes';

// Creer un preset personnalise base sur Aura
const MonTheme = definePreset(Aura, {
  semantic: {
    // Changer la couleur primaire du theme (bleu → violet)
    primary: {
      50:  '{violet.50}',
      100: '{violet.100}',
      200: '{violet.200}',
      300: '{violet.300}',
      400: '{violet.400}',
      500: '{violet.500}',
      600: '{violet.600}',
      700: '{violet.700}',
      800: '{violet.800}',
      900: '{violet.900}',
      950: '{violet.950}',
    },
    // Couleur de fond des inputs en dark mode
    colorScheme: {
      dark: {
        surface: {
          card: '{zinc.900}',
        },
      },
    },
  },
});

export const appConfig: ApplicationConfig = {
  providers: [
    provideAnimationsAsync(),
    providePrimeNG({
      theme: {
        preset: MonTheme,  // Utilise notre theme personnalise
        options: {
          darkModeSelector: '.dark', // Active dark mode via classe CSS
        },
      },
    }),
  ],
};

Basculer entre light et dark mode

// theme-toggle.component.ts
import { Component } from '@angular/core';
import { ButtonModule } from 'primeng/button';

@Component({
  selector: 'app-theme-toggle',
  standalone: true,
  imports: [ButtonModule],
  template: `
    <p-button
      [icon]="isDark ? 'pi pi-sun' : 'pi pi-moon'"
      [label]="isDark ? 'Mode clair' : 'Mode sombre'"
      variant="outlined"
      (onClick)="toggleTheme()"
    />
  `,
})
export class ThemeToggleComponent {
  isDark = false;

  toggleTheme(): void {
    this.isDark = !this.isDark;
    // Ajoute/retire la classe 'dark' sur le document.documentElement
    // Le darkModeSelector dans app.config.ts reference cette classe
    document.documentElement.classList.toggle('dark', this.isDark);
  }
}
Le systeme Design Token de PrimeNG 17+ est incompatible avec l'ancien systeme de variables CSS de PrimeNG <17. Si vous migrez d'une ancienne version, consultez le guide de migration officiel.

Composants avances

PrimeNG excelle dans les composants complexes que la plupart des librairies ne proposent pas. Voici les plus puissants avec leurs configurations les plus courantes.

FileUpload avec preview et drag-and-drop

// upload-documents.component.ts
import { Component } from '@angular/core';
import { FileUploadModule, FileUploadHandlerEvent } from 'primeng/fileupload';
import { ToastModule } from 'primeng/toast';
import { MessageService } from 'primeng/api';

@Component({
  selector: 'app-upload-documents',
  standalone: true,
  imports: [FileUploadModule, ToastModule],
  providers: [MessageService],
  template: `
    <p-toast />

    <!-- Upload avec drag-and-drop, preview et validation -->
    <p-fileupload
      name="documents"
      [url]="'/api/upload'"
      [multiple]="true"
      accept="image/*,.pdf,.docx"
      [maxFileSize]="5000000"
      [customUpload]="true"
      (uploadHandler)="onUpload($event)"
      (onError)="onError()"
    >
      <!-- Zone de drop personnalisee -->
      <ng-template pTemplate="empty">
        <div class="flex align-items-center justify-content-center flex-column p-4">
          <i class="pi pi-cloud-upload text-5xl text-muted-color mb-3"></i>
          <p>Glissez vos fichiers ici ou cliquez pour parcourir</p>
          <small class="text-muted">Images, PDF, DOCX — max 5 Mo par fichier</small>
        </div>
      </ng-template>
    </p-fileupload>
  `,
})
export class UploadDocumentsComponent {
  constructor(private messageService: MessageService) {}

  onUpload(event: FileUploadHandlerEvent): void {
    // Traitement personnalise des fichiers (appel HTTP manuel)
    const formData = new FormData();
    for (const file of event.files) {
      formData.append('file', file, file.name);
    }

    // Ici : envoyer formData via HttpClient
    // this.http.post('/api/upload', formData).subscribe(...)

    this.messageService.add({
      severity: 'success',
      summary: 'Upload reussi',
      detail: `${event.files.length} fichier(s) envoye(s)`,
    });
  }

  onError(): void {
    this.messageService.add({
      severity: 'error',
      summary: 'Erreur upload',
      detail: 'Le fichier depasse la taille maximale autorisee.',
    });
  }
}

Chart avec Chart.js (p-chart)

// dashboard-chart.component.ts
import { Component, OnInit, signal } from '@angular/core';
import { ChartModule } from 'primeng/chart';

@Component({
  selector: 'app-dashboard-chart',
  standalone: true,
  imports: [ChartModule],
  template: `
    <div class="card">
      <div class="card-header">
        <h5>Ventes mensuelles 2025</h5>
      </div>
      <div class="card-body">
        <!-- p-chart gere automatiquement Chart.js en interne -->
        <p-chart
          type="line"
          [data]="chartData()"
          [options]="chartOptions"
          [height]="'300px'"
        />
      </div>
    </div>
  `,
})
export class DashboardChartComponent implements OnInit {
  // Signal pour les donnees du graphique
  chartData = signal<any>(null);

  // Options Chart.js passees directement a PrimeNG
  chartOptions = {
    responsive: true,
    plugins: {
      legend: { position: 'top' },
      title: { display: false },
    },
    scales: {
      y: { beginAtZero: true },
    },
  };

  ngOnInit(): void {
    // Initialisation des donnees du graphique
    this.chartData.set({
      labels: ['Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Jun', 'Jul', 'Aou', 'Sep', 'Oct', 'Nov', 'Dec'],
      datasets: [
        {
          label: 'Ventes 2025',
          data: [12500, 18300, 15200, 22100, 19800, 25400, 28000, 24600, 21300, 27800, 31200, 35000],
          borderColor: 'rgb(99, 102, 241)', // Couleur de la ligne
          backgroundColor: 'rgba(99, 102, 241, 0.1)', // Remplissage sous la courbe
          fill: true,
          tension: 0.4, // Courbe lissee
        },
        {
          label: 'Ventes 2024',
          data: [10200, 14500, 13100, 17800, 16200, 19500, 22400, 20100, 18500, 23200, 26800, 29500],
          borderColor: 'rgb(234, 179, 8)',
          backgroundColor: 'rgba(234, 179, 8, 0.1)',
          fill: true,
          tension: 0.4,
        },
      ],
    });
  }
}

DatePicker (Calendar) avec localisation francaise

// date-picker-fr.component.ts
import { Component } from '@angular/core';
import { DatePickerModule } from 'primeng/datepicker';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-date-picker-fr',
  standalone: true,
  imports: [DatePickerModule, FormsModule],
  template: `
    <div class="field">
      <label for="dateNaissance">Date de naissance</label>

      <!-- DatePicker avec localisation francaise et plage de selection -->
      <p-datepicker
        [(ngModel)]="selectedDate"
        [showIcon]="true"
        [showButtonBar]="true"
        dateFormat="dd/mm/yy"
        placeholder="JJ/MM/AAAA"
        [monthNavigator]="true"
        [yearNavigator]="true"
        yearRange="1950:2010"
        [locale]="frLocale"
      />

      <!-- Affiche la date selectionnee -->
      @if (selectedDate) {
        <small class="text-muted mt-1">
          Date selectionnee : {{ selectedDate | date:'dd/MM/yyyy' }}
        </small>
      }
    </div>
  `,
})
export class DatePickerFrComponent {
  selectedDate: Date | null = null;

  // Configuration de la localisation francaise
  frLocale = {
    firstDayOfWeek: 1,         // Commence le lundi
    dayNames: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
    dayNamesShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
    dayNamesMin: ['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'],
    monthNames: ['Janvier', 'Fevrier', 'Mars', 'Avril', 'Mai', 'Juin',
                 'Juillet', 'Aout', 'Septembre', 'Octobre', 'Novembre', 'Decembre'],
    monthNamesShort: ['Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Jun',
                      'Jul', 'Aou', 'Sep', 'Oct', 'Nov', 'Dec'],
    today: "Aujourd'hui",
    clear: 'Effacer',
  };
}
Chart.js obligatoire : Le composant p-chart necessite l'installation de Chart.js : npm install chart.js. PrimeNG ne l'inclut pas dans ses dependances directes pour reduire la taille du bundle.

PrimeNG avec Angular Standalone

Depuis PrimeNG 17, chaque composant est exportable en tant que module standalone. Cela signifie qu'il n'est plus necessaire de creer un SharedModule ou d'importer des modules globaux. On importe uniquement ce dont on a besoin dans chaque composant.

Pattern recommande Angular 17+

// product-list.component.ts — composant standalone avec PrimeNG
import { Component, OnInit, signal, computed } from '@angular/core';
import { FormsModule } from '@angular/forms';

// Imports PrimeNG — uniquement ce qui est utilise
import { DataViewModule } from 'primeng/dataview';
import { ButtonModule } from 'primeng/button';
import { DropdownModule } from 'primeng/dropdown';
import { TagModule } from 'primeng/tag';
import { InputTextModule } from 'primeng/inputtext';

export interface Product {
  id: number;
  name: string;
  price: number;
  category: string;
  rating: number;
  inventoryStatus: 'INSTOCK' | 'LOWSTOCK' | 'OUTOFSTOCK';
  image: string;
}

@Component({
  selector: 'app-product-list',
  standalone: true,
  // Import direct — pas de SharedModule necessaire
  imports: [
    FormsModule,
    DataViewModule,
    ButtonModule,
    DropdownModule,
    TagModule,
    InputTextModule,
  ],
  template: `
    <!-- Barre d'outils : tri et basculement vue grille/liste -->
    <p-dataview
      [value]="filteredProducts()"
      [layout]="layout"
    >
      <ng-template pTemplate="header">
        <div class="flex justify-content-between">
          <p-dropdown
            [options]="sortOptions"
            [(ngModel)]="sortKey"
            placeholder="Trier par"
            (onChange)="onSortChange($event)"
          />
          <!-- Boutons pour basculer entre vue grille et liste -->
          <div class="flex gap-1">
            <p-button
              icon="pi pi-th-large"
              [text]="layout !== 'grid'"
              (onClick)="layout = 'grid'"
            />
            <p-button
              icon="pi pi-list"
              [text]="layout !== 'list'"
              (onClick)="layout = 'list'"
            />
          </div>
        </div>
      </ng-template>

      <!-- Template vue grille -->
      <ng-template pTemplate="grid" let-products>
        <div class="grid grid-nogutter">
          @for (product of products; track product.id) {
            <div class="col-12 md:col-4 p-2">
              <div class="p-4 border-1 surface-border border-round">
                <img [src]="product.image" [alt]="product.name" class="w-full mb-3" />
                <div class="font-bold mb-1">{{ product.name }}</div>
                <div class="text-primary font-bold">{{ product.price | currency:'EUR' }}</div>
                <p-tag
                  [value]="product.inventoryStatus"
                  [severity]="getStatusSeverity(product.inventoryStatus)"
                  class="mt-2"
                />
              </div>
            </div>
          }
        </div>
      </ng-template>
    </p-dataview>
  `,
})
export class ProductListComponent implements OnInit {
  products = signal<Product[]>([]);
  searchTerm = signal<string>('');
  sortKey = '';
  layout: 'list' | 'grid' = 'grid';

  // Filtre reactif base sur les signals
  filteredProducts = computed(() => {
    const term = this.searchTerm().toLowerCase();
    if (!term) return this.products();
    return this.products().filter(p =>
      p.name.toLowerCase().includes(term) ||
      p.category.toLowerCase().includes(term)
    );
  });

  sortOptions = [
    { label: 'Prix croissant', value: 'price' },
    { label: 'Prix decroissant', value: '!price' },
    { label: 'Nom A-Z', value: 'name' },
  ];

  ngOnInit(): void {
    // Donnees de demonstration
    this.products.set([
      { id: 1, name: 'Casque audio', price: 89.99, category: 'Audio',
        rating: 4, inventoryStatus: 'INSTOCK', image: 'assets/casque.jpg' },
      { id: 2, name: 'Souris ergonomique', price: 49.99, category: 'Peripheriques',
        rating: 5, inventoryStatus: 'LOWSTOCK', image: 'assets/souris.jpg' },
    ]);
  }

  getStatusSeverity(status: string): 'success' | 'warning' | 'danger' {
    const map: Record<string, 'success' | 'warning' | 'danger'> = {
      INSTOCK: 'success',
      LOWSTOCK: 'warning',
      OUTOFSTOCK: 'danger',
    };
    return map[status] ?? 'info';
  }

  onSortChange(event: any): void {
    // Logique de tri — a implementer selon le besoin
    console.log('Trier par :', event.value);
  }
}
Bonnes pratiques standalone avec PrimeNG :
  • Importer uniquement les modules necessaires dans le tableau imports
  • Ne jamais creer un SharedModule qui importe tous les composants PrimeNG en bloc
  • Combiner les composants PrimeNG avec les Signals Angular pour la reactivite
  • Fournir MessageService et ConfirmationService dans le composant ou au niveau de l'application
  • Placer p-toast et p-confirmDialog dans le template du composant racine (app.component.html)

Performance et bonnes pratiques

PrimeNG est optimise pour la performance, mais quelques configurations cles permettent de tirer le meilleur parti de la librairie dans les applications a fort volume de donnees.

Virtual Scroll pour les grandes listes

Avec des milliers de lignes, le rendu DOM complet est prohibitif. p-table integre un virtual scroll qui ne rend que les lignes visibles dans le viewport.

// large-dataset-table.component.ts
import { Component, OnInit, signal } from '@angular/core';
import { TableModule } from 'primeng/table';

interface LogEntry {
  id: number;
  timestamp: string;
  level: 'INFO' | 'WARN' | 'ERROR';
  message: string;
}

@Component({
  selector: 'app-large-dataset-table',
  standalone: true,
  imports: [TableModule],
  template: `
    <p-table
      [value]="logs()"
      [scrollable]="true"
      scrollHeight="400px"
      [virtualScroll]="true"
      [virtualScrollItemSize]="46"
      [rows]="50"
    >
      <ng-template pTemplate="header">
        <tr>
          <th>Timestamp</th>
          <th>Niveau</th>
          <th>Message</th>
        </tr>
      </ng-template>

      <!-- Le virtual scroll ne rend que les lignes visibles -->
      <ng-template pTemplate="body" let-log let-rowIndex="rowIndex">
        <tr [style.height]="'46px'">
          <td>{{ log.timestamp }}</td>
          <td>{{ log.level }}</td>
          <td>{{ log.message }}</td>
        </tr>
      </ng-template>
    </p-table>
  `,
})
export class LargeDatasetTableComponent implements OnInit {
  logs = signal<LogEntry[]>([]);

  ngOnInit(): void {
    // Generation de 10 000 entrees de log pour la demonstration
    this.logs.set(
      Array.from({ length: 10000 }, (_, i) => ({
        id: i + 1,
        timestamp: new Date(Date.now() - i * 1000).toISOString(),
        level: (['INFO', 'WARN', 'ERROR'] as const)[i % 3],
        message: `Evenement systeme #${i + 1} traite avec succes`,
      }))
    );
  }
}

Lazy Loading avec p-table et API serveur

// server-side-table.component.ts
import { Component, signal } from '@angular/core';
import { TableModule, TableLazyLoadEvent } from 'primeng/table';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-server-side-table',
  standalone: true,
  imports: [TableModule],
  template: `
    <p-table
      [value]="rows()"
      [lazy]="true"
      [paginator]="true"
      [rows]="pageSize"
      [totalRecords]="totalRecords()"
      [loading]="loading()"
      (onLazyLoad)="loadData($event)"
    >
      <ng-template pTemplate="header">
        <tr><th pSortableColumn="name">Nom <p-sortIcon field="name"/></th></tr>
      </ng-template>
      <ng-template pTemplate="body" let-row>
        <tr><td>{{ row.name }}</td></tr>
      </ng-template>
    </p-table>
  `,
})
export class ServerSideTableComponent {
  rows = signal<any[]>([]);
  totalRecords = signal<number>(0);
  loading = signal<boolean>(false);
  pageSize = 15;

  constructor(private http: HttpClient) {}

  loadData(event: TableLazyLoadEvent): void {
    this.loading.set(true);

    // Construire les parametres de l'API a partir de l'evenement PrimeNG
    const params = {
      first:    String(event.first ?? 0),        // Index de depart
      rows:     String(event.rows ?? this.pageSize), // Nombre de lignes
      sortField: String(event.sortField ?? ''),   // Champ de tri
      sortOrder: String(event.sortOrder ?? '1'),  // 1=ASC, -1=DESC
    };

    this.http.get<{ data: any[]; total: number }>('/api/users', { params })
      .subscribe({
        next: (res) => {
          this.rows.set(res.data);          // Donnees de la page courante
          this.totalRecords.set(res.total); // Total pour la pagination
          this.loading.set(false);
        },
        error: () => this.loading.set(false),
      });
  }
}

OnPush Change Detection

// optimized-card.component.ts
import { Component, input, ChangeDetectionStrategy } from '@angular/core';
import { CardModule } from 'primeng/card';

@Component({
  selector: 'app-optimized-card',
  standalone: true,
  imports: [CardModule],
  // OnPush : Angular ne verifie ce composant que si ses inputs changent
  // Ideal pour les composants presentationnels avec des donnees immuables
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <p-card [header]="title()" [subheader]="subtitle()">
      <p>{{ content() }}</p>
    </p-card>
  `,
})
export class OptimizedCardComponent {
  title    = input.required<string>();
  subtitle = input<string>('');
  content  = input.required<string>();
}
Regles de performance PrimeNG : Activez toujours [lazy]="true" pour les tables avec plus de 500 lignes. Utilisez [virtualScroll]="true" pour les listes deroule. Activez ChangeDetectionStrategy.OnPush sur tous les composants presentationnels.

PrimeNG vs Angular Material

Le choix entre PrimeNG et Angular Material depend du contexte du projet. Les deux librairies sont de qualite professionnelle, activement maintenues et compatibles Angular 17+. Voici une comparaison objective.

Critere PrimeNG Angular Material
Nombre de composants 90+ composants ~35 composants
DataTable avance Oui — pagination, tri, filtres, virtual scroll, edition inline Basique — mat-table sans fonctionnalites avancees
Charts / Graphiques Oui — integration Chart.js via p-chart Non — necessite une librairie tierce
Upload de fichiers Oui — drag & drop, preview, validation Non
Systeme de themes Design Tokens, 28+ themes pre-faits, personnalisation fine Material Design 3, theming via SCSS variables
Coherence designee Plusieurs styles (Aura, Lara, Nora) — flexibilite totale Material Design strict — coherent mais moins flexible
Accessibilite WCAG 2.1 AA sur tous les composants WCAG 2.1 AA sur tous les composants
Taille du bundle Equivalent — tree-shaking efficace avec standalone Equivalent — tree-shaking efficace
Documentation Excellente — exemples interactifs pour chaque composant Excellente — examples et API reference complets
Cas d'usage ideal ERP, CRM, dashboards, applications de gestion complexes Applications Google-like, apps consumer, mobile-first

Quand choisir PrimeNG ?

  • Votre application affiche des tables de donnees complexes avec tri, filtres et pagination
  • Vous avez besoin de composants specialises (TreeTable, FileUpload, Chart, Scheduler)
  • Votre equipe veut une liberte de design sans etre lie au Material Design
  • Vous developpez un back-office, ERP ou CRM

Quand choisir Angular Material ?

  • Votre application doit suivre les guidelines Material Design (applications Google-like)
  • Votre application est mobile-first ou grand public
  • Vous privilegiez l'integration ecosysteme Google (Firebase, Google Maps)
  • Votre equipe est deja familiere avec Material Design

Migration Angular Material vers PrimeNG

// Exemple de remplacement courant
// AVANT : Angular Material
// <mat-button color="primary">Sauvegarder</mat-button>

// APRES : PrimeNG — meme semantique, syntaxe differente
// <p-button label="Sauvegarder" severity="primary" />

// AVANT : Angular Material table
// <mat-table [dataSource]="dataSource">...</mat-table>

// APRES : PrimeNG table avec fonctionnalites bonus
// <p-table [value]="data" [paginator]="true" [rows]="10">...</p-table>

// AVANT : Angular Material dialog
// this.dialog.open(MonDialog, { data: {...} })

// APRES : PrimeNG dialog via [(visible)]
// dialogVisible = true dans le composant + <p-dialog [(visible)]="dialogVisible">
Il n'existe pas de schematics de migration automatique depuis Angular Material vers PrimeNG. La migration est manuelle, composant par composant. Commencez par identifier les composants les plus utilises et migrez-les progressivement.

Conclusion

PrimeNG s'impose comme la librairie UI la plus complete de l'ecosysteme Angular. Avec plus de 90 composants, un systeme de themes flexible base sur les Design Tokens, et un support natif des composants standalone Angular 17+, elle couvre la quasi-totalite des besoins d'une application professionnelle sans devoir assembler plusieurs librairies.

L'approche recommended est d'installer PrimeNG des le debut d'un projet Angular, de configurer le theme une seule fois dans app.config.ts, puis d'importer uniquement les composants necessaires dans chaque fichier. Grace au tree-shaking, seul le code reellement utilise est inclus dans le bundle final. Les composants avances comme p-table avec virtual scroll et lazy loading serveur, ou p-chart avec Chart.js, permettent de construire des interfaces de gestion sophistiquees en quelques dizaines de lignes de code.

A retenir : PrimeNG est le choix optimal pour les applications Angular de gestion (ERP, CRM, dashboards). Installez-le avec npm install primeng primeicons, configurez le theme Aura dans app.config.ts, et importez uniquement les composants dont vous avez besoin — le tree-shaking fait le reste.

Partager