@suntelecoms/ngx-dynamic-form
v1.3.2
Published
Générateur de formulaires Angular dynamiques — SUNTELECOMS
Maintainers
Readme
@suntelecoms/ngx-dynamic-form
Génère des formulaires Angular complexes à partir d'une configuration TypeScript — sans écrire une ligne de template HTML. Inclut un Form Builder visuel, la gestion des schémas sauvegardés, un système de stockage interchangeable (localStorage / API REST) et le support des formulaires multi-étapes (Stepper).
Sommaire
- Présentation
- Fonctionnalités
- Installation
- Démarrage rapide — formulaire statique
- Démarrage rapide — stack complète
- Types de champs
- Configuration — DynamicFormConfig
- Propriétés d'un champ — DynamicFormField
- Options dynamiques depuis une API
- Validation
- Affichage conditionnel — showWhen
- Icônes (Material Icons)
- Disposition en grille (col)
- API du composant DynamicForm
- Formulaires multi-étapes — Stepper
- provideNgxForms — configuration globale
- FormConfigService — gestion des schémas
- Stockage — LocalStorage vs API REST
- Form Builder — éditeur visuel
- Composants de navigation
- Intégration dans un composant
- Pages de démonstration
- Exemples complets
- Personnalisation CSS
- Interfaces TypeScript
- Auteur
Présentation
@suntelecoms/ngx-dynamic-form est un plugin Angular 19 qui génère des formulaires réactifs dynamiquement à partir d'un objet de configuration TypeScript. Il s'appuie sur Angular Reactive Forms et Angular Material.
Depuis la v1.2.0, le plugin embarque :
- Le Form Builder visuel (
<ngx-form-builder>) — avec mode Stepper - Le gestionnaire de formulaires (
<ngx-mes-formulaires>) - La page de rendu par code (
<ngx-form-loader>) - Un système de stockage abstrait (localStorage par défaut, API REST Spring en option)
- Les formulaires multi-étapes (
<ngx-stepper-form>) avec étape de récapitulatif
Résultat : dans n'importe quel projet Angular, il suffit de 2 lignes pour avoir une interface complète de paramétrage de formulaires.
Fonctionnalités
- 24 types de champs —
text,email,password,number,tel,url,date,datetime-local,time,month,week,textarea,select,multiselect,radio,checkbox,switch,range,color,location,file,hidden,divider,heading - 3 variantes d'apparence —
outline,fill,simple(label fixe au-dessus) — globale ou par champ (v1.2.7) - Datepicker Material (
mat-datepicker) pour le typedate(v1.2.8) - Géolocalisation — type
locationavec bouton GPS et APInavigator.geolocation(v1.2.8) - Curseur de valeur — type
rangerendu enmat-slider(v1.2.8) - Interrupteur — type
switchrendu enmat-slide-toggle(v1.2.8) - Styling par champ —
fontSize,textAlign,color,backgroundColor,borderColor,labelColor,fontWeight,fontStyle,cssClass,styles(v1.2.7) - Masque de saisie — propriété
mask(#= chiffre, autres chars = séparateurs) (v1.2.7) - Préfixe / suffixe texte — propriétés
prefixetsuffix(v1.2.7) - Code unique par formulaire — propriété
codesaisie par l'utilisateur, utilisée pour l'URL de partage et la récupération par code (v1.2.9) - Authentification Bearer token —
authdansprovideNgxForms+ intercepteurngxFormsAuthInterceptorpour les APIs sécurisées (login/mot de passe → JWT) (v1.3.0) - Floating label Angular Material (
mat-form-field appearance="outline") - Validation déclarative —
required,email,minLength,maxLength,min,max,pattern, validateur personnalisé - Messages d'erreur personnalisés par champ
- Affichage conditionnel (
showWhen) avec 7 opérateurs - Conditions multiples (logique AND)
- Grille 12 colonnes — propriété
col - Icônes Material Icons — préfixe et suffixe par champ
- Préremplissage via
[initialValues] - Mode lecture seule / désactivé par champ
- Sections et séparateurs (
heading,divider) - API publique —
getForm(),patchValues()via@ViewChild - Événements —
formSubmit,formChange,formReset - Formulaires multi-étapes (Stepper) —
<ngx-stepper-form>avec navigation, validation par étape et récapitulatif automatique (v1.2.0) - Form Builder visuel — interface 3 panneaux, supporte les modes Formulaire et Stepper (v1.2.0)
- Options dynamiques depuis une API — propriétés
url,displayName,displayValuesur les champsselect,multiselect,radio(v1.2.5) - Stockage abstrait — localStorage ou API REST Spring
- FormConfigService — Signal-based, Observable API
Installation
1. Prérequis
- Angular 19+
- Node.js 18+
2. Installer les dépendances
npm install @suntelecoms/ngx-dynamic-form @angular/material@^19.0.0 @angular/cdk@^19.0.03. Ajouter le thème Material et les icônes
angular.json :
"styles": [
"@angular/material/prebuilt-themes/azure-blue.css",
"src/styles.css"
]index.html :
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">4. Configurer le plugin (app.config.ts)
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideAnimations } from '@angular/platform-browser/animations';
import { provideNgxForms } from '@suntelecoms/ngx-dynamic-form';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideAnimations(),
provideNgxForms({ storage: 'local' }), // ← stockage localStorage
],
};5. Ajouter les routes (app.routes.ts)
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'builder',
loadComponent: () => import('@suntelecoms/ngx-dynamic-form').then(m => m.FormBuilderComponent),
},
{
path: 'mes-formulaires',
loadComponent: () => import('@suntelecoms/ngx-dynamic-form').then(m => m.MesFormulairesComponent),
},
{
path: 'form/:code', // ← le formulaire est identifié par son code unique
loadComponent: () => import('@suntelecoms/ngx-dynamic-form').then(m => m.FormLoaderComponent),
},
];C'est tout. Aucun code supplémentaire à écrire — le Builder, la liste des formulaires et la page de rendu sont entièrement fournis par le plugin.
Démarrage rapide — formulaire statique
Utilisez <ngx-dynamic-form> directement avec une config TypeScript :
import { Component } from '@angular/core';
import { DynamicFormComponent } from '@suntelecoms/ngx-dynamic-form';
import type { DynamicFormConfig, DynamicFormSubmitEvent } from '@suntelecoms/ngx-dynamic-form';
@Component({
selector: 'app-contact',
standalone: true,
imports: [DynamicFormComponent],
template: `
<ngx-dynamic-form [config]="config" (formSubmit)="onSubmit($event)" />
`,
})
export class ContactComponent {
config: DynamicFormConfig = {
submitLabel: 'Envoyer',
showReset: true,
fields: [
{ key: 'name', type: 'text', label: 'Nom complet', col: 6, icon: 'person',
validation: { required: true } },
{ key: 'email', type: 'email', label: 'E-mail', col: 6, icon: 'email',
validation: { required: true, email: true } },
{ key: 'message', type: 'textarea', label: 'Message', rows: 5,
validation: { required: true } },
],
};
onSubmit(event: DynamicFormSubmitEvent): void {
console.log(event.value);
}
}Démarrage rapide — stack complète
Pour disposer du Form Builder + gestionnaire + rendu par URL dans votre application, il suffit des 2 fichiers de configuration vus à l'installation. Après ng serve :
| URL | Page |
|---|---|
| /builder | Interface graphique de création de formulaires (simple ou stepper) |
| /mes-formulaires | Liste des formulaires sauvegardés, avec rendu à la volée |
| /form/mon-code | Rendu d'un formulaire sauvegardé par son code unique |
Types de champs
Saisie texte
| Type | Rendu | Description |
|---|---|---|
| text | <input type="text"> | Champ texte standard |
| email | <input type="email"> | Validation e-mail intégrée |
| password | <input type="password"> | Mot de passe masqué |
| number | <input type="number"> | Supporte min, max |
| tel | <input type="tel"> | Téléphone — compatible masque de saisie |
| url | <input type="url"> | URL |
| textarea | <textarea> | Zone de texte multi-lignes, propriété rows |
Date & Heure
| Type | Rendu | Description |
|---|---|---|
| date | mat-datepicker | Calendrier Material — popup avec sélection mois/année (v1.2.8) |
| datetime-local | <input type="datetime-local"> | Date et heure combinées |
| time | <input type="time"> | Heure (HH:mm) |
| month | <input type="month"> | Mois et année |
| week | <input type="week"> | Semaine ISO |
Choix
| Type | Rendu | Description |
|---|---|---|
| select | <mat-select> | Liste déroulante — 1 choix — options statiques ou API |
| multiselect | <mat-select multiple> | Liste déroulante — plusieurs choix |
| radio | <mat-radio-group> | Boutons radio — options statiques ou API |
| checkbox | <mat-checkbox> | Case à cocher booléenne |
| switch | <mat-slide-toggle> | Interrupteur on/off (v1.2.8) |
Spéciaux
| Type | Rendu | Description |
|---|---|---|
| range | <mat-slider> | Curseur de valeur — propriétés min, max, step, showValue (v1.2.8) |
| color | <input type="color"> | Sélecteur de couleur avec affichage du code hex (v1.2.8) |
| location | <input> + bouton GPS | Champ adresse avec géolocalisation — bouton my_location déclenche navigator.geolocation (v1.2.8) |
| file | <input type="file"> | Téléversement — propriétés accept, multiple |
Mise en page
| Type | Rendu | Description |
|---|---|---|
| hidden | <input type="hidden"> | Champ caché (présent dans le FormGroup) |
| divider | <hr> | Séparateur horizontal |
| heading | <h1> à <h6> | Titre de section — propriétés text, level |
Configuration — DynamicFormConfig
interface DynamicFormConfig {
fields: DynamicFormField[]; // Liste des champs (obligatoire)
submitLabel?: string; // Libellé du bouton Envoyer (défaut : "Envoyer")
resetLabel?: string; // Libellé du bouton Réinitialiser (défaut : "Réinitialiser")
showReset?: boolean; // Afficher le bouton Réinitialiser (défaut : false)
layout?: 'vertical' | 'horizontal' | 'inline';
cssClass?: string;
debug?: boolean; // Affiche les valeurs JSON en temps réel
// Apparence globale ← v1.2.7
appearance?: 'outline' | 'fill' | 'simple'; // défaut : 'outline'
floatLabel?: 'auto' | 'always'; // défaut : 'auto'
}Propriétés d'un champ — DynamicFormField
interface DynamicFormField {
// Obligatoire
key: string; // Identifiant unique (clé dans FormGroup)
type: FieldType;
// Affichage
label?: string;
placeholder?: string;
hint?: string;
icon?: string; // Icône préfixe (Material Icons)
iconSuffix?: string; // Icône suffixe (Material Icons)
prefix?: string; // Texte préfixe dans le champ (ex : '+221') ← v1.2.7
suffix?: string; // Texte suffixe dans le champ (ex : 'FCFA') ← v1.2.7
// Valeur & état
defaultValue?: any;
disabled?: boolean;
readonly?: boolean;
hidden?: boolean;
// Options statiques (select / multiselect / radio)
options?: SelectOption[];
// Options dynamiques chargées depuis une API ← v1.2.5
url?: string; // endpoint relatif ('villes') ou absolu ('https://...')
displayName?: string; // propriété de la réponse API utilisée comme label (défaut : 'label')
displayValue?: string; // propriété de la réponse API utilisée comme valeur (défaut : 'value')
// Validation
validation?: FieldValidation;
// Disposition
col?: 1|2|3|4|6|8|9|12; // Colonnes sur 12 (défaut : 12)
cssClass?: string; // Classe CSS ajoutée sur l'hôte ← v1.2.7
// Affichage conditionnel
showWhen?: FieldCondition | FieldCondition[];
// Spécifique file
accept?: string; // Ex : ".pdf,.jpg"
multiple?: boolean;
// Spécifique textarea
rows?: number; // Défaut : 3
// Spécifique heading
text?: string;
level?: 1|2|3|4|5|6; // Défaut : 2
// Spécifique range ← v1.2.8
min?: number; // Valeur minimale du curseur (défaut : 0)
max?: number; // Valeur maximale du curseur (défaut : 100)
step?: number; // Pas du curseur (défaut : 1)
showValue?: boolean; // Afficher la valeur courante à côté du curseur
// Masque de saisie ← v1.2.7
// '#' = chiffre obligatoire, tout autre caractère = séparateur littéral
// Ex : '## ### ## ##' → téléphone SN, '##/##/####' → date, '#### #### #### ####' → CB
mask?: string;
// Apparence individuelle (override la valeur globale du formulaire) ← v1.2.7
appearance?: 'outline' | 'fill' | 'simple';
floatLabel?: 'auto' | 'always';
// Style & Apparence ← v1.2.7 / v1.2.8
fontSize?: string; // Ex : '14px', '1.1rem'
textAlign?: 'left' | 'center' | 'right';
color?: string; // Couleur du texte saisi
backgroundColor?: string; // Couleur de fond du champ
borderColor?: string; // Couleur de la bordure ← v1.2.8
labelColor?: string; // Couleur du label ← v1.2.8
fontWeight?: 'normal' | 'bold';
fontStyle?: 'normal' | 'italic';
styles?: Record<string, string>; // Styles CSS inline supplémentaires
attrs?: Record<string, any>;
}SelectOption
interface SelectOption {
label: string;
value: any;
disabled?: boolean;
group?: string;
}Options dynamiques depuis une API
Nouveauté v1.2.5
Pour les champs select, multiselect et radio, les options peuvent être chargées automatiquement depuis une API REST au lieu d'être définies statiquement. Il suffit d'utiliser url à la place de options.
Comment ça fonctionne
- Le composant détecte la propriété
urlsur le champ - Il effectue un
GETsur l'URL complète (dataUrl+url) - Il mappe chaque objet de la réponse en
{ label, value }en utilisantdisplayNameetdisplayValue - Pendant le chargement, le select affiche "Chargement…" avec un spinner
Configuration dans app.config.ts
import { provideHttpClient } from '@angular/common/http';
import { provideNgxForms } from '@suntelecoms/ngx-dynamic-form';
providers: [
provideHttpClient(), // requis pour les options depuis API
provideNgxForms({
storage: 'local',
dataUrl: 'https://api.monprojet.com', // base URL pour les options
}),
]Exemples
Options depuis une API (URL relative) :
{
key: 'ville',
type: 'select',
label: 'Ville',
col: 6,
icon: 'location_city',
url: 'villes', // → GET https://api.monprojet.com/villes
displayName: 'nom', // propriété affichée dans la liste
displayValue: 'id', // propriété utilisée comme valeur du formulaire
validation: { required: true },
}Options depuis une API (URL absolue) :
{
key: 'compte',
type: 'select',
label: 'Compte',
col: 6,
url: 'https://autre-api.com/comptes', // URL absolue — dataUrl ignoré
displayName: 'numero',
displayValue: 'numero',
validation: { required: true },
}Multi-sélection depuis une API :
{
key: 'categories',
type: 'multiselect',
label: 'Catégories',
col: 12,
url: 'categories',
displayName: 'libelle',
displayValue: 'code',
}Réponse API attendue
L'API doit retourner un tableau JSON d'objets :
[
{ "id": 1, "nom": "Dakar" },
{ "id": 2, "nom": "Thiès" },
{ "id": 3, "nom": "Saint-Louis" }
]Avec displayName: 'nom' et displayValue: 'id', le select affichera Dakar, Thiès, Saint-Louis et soumettra 1, 2, 3.
Résumé des propriétés
| Propriété | Type | Description |
|---|---|---|
| url | string | Endpoint relatif ('villes') ou absolu ('https://...'). Quand défini, options est ignoré. |
| displayName | string | Propriété de l'objet API utilisée comme label affiché (défaut : 'label') |
| displayValue | string | Propriété de l'objet API utilisée comme valeur du formulaire (défaut : 'value') |
Note :
urletoptionssont mutuellement exclusifs. Siurlest défini,optionsest ignoré.
Validation
interface FieldValidation {
required?: boolean;
email?: boolean;
minLength?: number;
maxLength?: number;
min?: number;
max?: number;
pattern?: string | RegExp;
custom?: (value: any) => string | null;
messages?: {
required?: string;
email?: string;
minLength?: string;
maxLength?: string;
min?: string;
max?: string;
pattern?: string;
};
}Exemples :
// Validateur avec message personnalisé
{
key: 'email',
type: 'email',
label: 'E-mail',
validation: {
required: true,
email: true,
messages: {
required: 'L\'e-mail est obligatoire.',
email: 'Format e-mail invalide.',
},
},
}
// Validateur custom
{
key: 'password',
type: 'password',
label: 'Mot de passe',
validation: {
required: true,
custom: (v) => {
if (!v || v.length < 8) return 'Minimum 8 caractères.';
if (!/[A-Z]/.test(v)) return 'Au moins une majuscule.';
return null;
},
},
}Affichage conditionnel — showWhen
Un champ peut être affiché ou masqué en fonction de la valeur d'un autre champ, sans écrire de code Angular.
// Condition unique — afficher "Entreprise" seulement si accountType === 'pro'
{
key: 'company',
type: 'text',
label: 'Entreprise',
showWhen: { field: 'accountType', operator: 'equals', value: 'pro' },
}
// Conditions multiples (logique AND)
{
key: 'discount',
type: 'number',
label: 'Remise (%)',
showWhen: [
{ field: 'accountType', operator: 'equals', value: 'pro' },
{ field: 'orderTotal', operator: 'greaterThan', value: 500 },
],
}Opérateurs disponibles
| Opérateur | Description |
|---|---|
| equals | Valeur exactement égale |
| notEquals | Valeur différente |
| contains | Contient (string ou tableau) |
| greaterThan | Valeur numérique supérieure |
| lessThan | Valeur numérique inférieure |
| exists | Champ renseigné (non vide, non null) |
| notExists | Champ vide ou null |
Icônes (Material Icons)
{ key: 'email', type: 'email', label: 'E-mail', icon: 'email', iconSuffix: 'verified' }| Champ | Icône recommandée |
|---|---|
| Nom, Prénom | person, badge |
| E-mail | email |
| Téléphone | phone |
| Mot de passe | lock |
| Adresse | home, location_on |
| Entreprise | business |
| Prix | euro, attach_money |
| Date | calendar_today |
| Pièce jointe | attach_file |
Disposition en grille (col)
La propriété col détermine le nombre de colonnes sur 12 occupées par le champ.
| col | Largeur | Usage |
|---|---|---|
| 12 | 100% | Pleine largeur (défaut) |
| 6 | 50% | Deux champs côte à côte |
| 4 | 33% | Trois champs côte à côte |
| 3 | 25% | Quatre champs côte à côte |
En dessous de 600 px, tous les champs passent automatiquement en pleine largeur.
Exemple avec sections :
fields: [
{ key: '_s1', type: 'heading', text: 'Informations', level: 3, col: 12 },
{ key: 'name', type: 'text', label: 'Nom', col: 6 },
{ key: 'email', type: 'email', label: 'E-mail', col: 6 },
{ key: '_div', type: 'divider', col: 12 },
{ key: '_s2', type: 'heading', text: 'Adresse', level: 3, col: 12 },
]API du composant DynamicForm
Inputs
| Propriété | Type | Description |
|---|---|---|
| [config] | DynamicFormConfig | Configuration du formulaire (obligatoire) |
| [initialValues] | Record<string, any> | Préremplissage des champs (mode édition) |
Outputs
| Événement | Type | Description |
|---|---|---|
| (formSubmit) | DynamicFormSubmitEvent | Formulaire soumis et valide |
| (formChange) | Record<string, any> | Émis à chaque modification |
| (formReset) | void | Formulaire réinitialisé |
API publique via @ViewChild
@ViewChild('f') formRef!: DynamicFormComponent;
// Accéder au FormGroup Angular
const fg = this.formRef.getForm();
console.log(fg.value, fg.valid);
// Mettre à jour des valeurs sans réinitialiser
this.formRef.patchValues({ name: 'Nouveau nom' });<ngx-dynamic-form
#f
[config]="config"
[initialValues]="existingData"
(formSubmit)="onSubmit($event)"
(formChange)="onChange($event)"
/>Formulaires multi-étapes — Stepper
Nouveauté v1.2.0
Un formulaire stepper découpe la saisie en plusieurs étapes numérotées. L'utilisateur avance étape par étape — chaque étape est validée avant de passer à la suivante. À la fin, une étape Récapitulatif affiche toutes les valeurs saisies avec la possibilité de revenir corriger une étape.
Comment ça fonctionne
Étape 1 Étape 2 Étape 3 Récapitulatif
Identité ──► Adresse ──► Documents ──► Vérification + Envoi- Navigation linéaire : impossible de passer à l'étape suivante si l'étape en cours est invalide.
- Bouton Modifier dans le récapitulatif : permet de revenir à n'importe quelle étape.
- Un seul
formSubmit: les données de toutes les étapes sont fusionnées dans un seul objet au moment de l'envoi.
Utilisation du composant <ngx-stepper-form>
import { Component } from '@angular/core';
import { StepperFormComponent } from '@suntelecoms/ngx-dynamic-form';
import type { StepperFormConfig, DynamicFormSubmitEvent } from '@suntelecoms/ngx-dynamic-form';
@Component({
selector: 'app-inscription',
standalone: true,
imports: [StepperFormComponent],
template: `
<ngx-stepper-form [config]="config" (formSubmit)="onSubmit($event)" />
`,
})
export class InscriptionComponent {
config: StepperFormConfig = {
showSummary: true,
submitLabel: 'Valider',
steps: [
{
label: 'Identité',
icon: 'person',
fields: [
{ key: 'firstName', type: 'text', label: 'Prénom', col: 6, validation: { required: true } },
{ key: 'lastName', type: 'text', label: 'Nom', col: 6, validation: { required: true } },
{ key: 'email', type: 'email', label: 'E-mail', col: 12, validation: { required: true, email: true } },
],
},
{
label: 'Adresse',
icon: 'home',
fields: [
{ key: 'street', type: 'text', label: 'Rue', col: 12, validation: { required: true } },
{ key: 'city', type: 'text', label: 'Ville', col: 6, validation: { required: true } },
{ key: 'zip', type: 'text', label: 'Code postal', col: 6, validation: { required: true } },
],
},
{
label: 'Confirmation',
icon: 'check_circle',
fields: [
{ key: 'terms', type: 'checkbox', placeholder: "J'accepte les CGU", col: 12,
validation: { required: true } },
],
},
],
};
onSubmit(event: DynamicFormSubmitEvent): void {
console.log(event.value);
}
}Configuration — StepperFormConfig
interface StepperFormConfig {
steps: FormStep[]; // Liste des étapes (obligatoire)
submitLabel?: string; // Libellé du bouton Envoyer (défaut : "Envoyer")
showReset?: boolean; // Afficher Réinitialiser (défaut : false)
resetLabel?: string; // Libellé du bouton Reset
linear?: boolean; // Bloque la navigation si l'étape est invalide (défaut : true)
orientation?: 'horizontal' | 'vertical';
showSummary?: boolean; // Ajoute une étape Récapitulatif à la fin (défaut : false)
summaryLabel?: string; // Libellé de l'étape récapitulatif (défaut : "Récapitulatif")
cssClass?: string;
debug?: boolean;
}Configuration — FormStep
interface FormStep {
label: string; // Titre de l'étape affiché dans la barre de navigation
icon?: string; // Icône Material Icons (optionnelle)
description?: string; // Sous-titre de l'étape (optionnel)
fields: DynamicFormField[];
}L'étape Récapitulatif
Quand showSummary: true, une étape supplémentaire est ajoutée automatiquement à la fin du stepper. Elle affiche :
- Toutes les étapes sous forme de sections distinctes
- Pour chaque champ rempli : son label et sa valeur formatée
- Un bouton Modifier par étape pour naviguer directement vers elle
- Les types spéciaux sont formatés intelligemment :
checkbox→ "Oui" / "Non"select/radio→ le label de l'option (pas la valeur brute)date→ formatjj/mm/aaaapassword→••••••••multiselect→ labels séparés par des virgules
Inputs de StepperFormComponent
| Propriété | Type | Description |
|---|---|---|
| [config] | StepperFormConfig | Configuration du stepper (obligatoire) |
| [initialValues] | Record<string, any> | Préremplissage des champs |
Outputs de StepperFormComponent
| Événement | Type | Description |
|---|---|---|
| (formSubmit) | DynamicFormSubmitEvent | Toutes les étapes valides — données fusionnées |
| (formChange) | Record<string, any> | Émis à chaque modification (toutes étapes) |
| (stepChange) | number | Index de la nouvelle étape active |
provideNgxForms — configuration globale
provideNgxForms() est la fonction de configuration du plugin. Elle enregistre le FormConfigService et l'adaptateur de stockage dans le contexte Angular.
Stockage localStorage (défaut)
// app.config.ts
import { provideHttpClient } from '@angular/common/http';
import { provideNgxForms } from '@suntelecoms/ngx-dynamic-form';
providers: [
provideNgxForms({ storage: 'local' }),
]Avec options dynamiques depuis une API :
providers: [
provideHttpClient(),
provideNgxForms({
storage: 'local',
dataUrl: 'https://api.monprojet.com',
}),
]Stockage API REST (Spring Boot) — API ouverte
// app.config.ts
providers: [
provideHttpClient(),
provideNgxForms({
storage: 'http',
apiUrl: 'https://api.monprojet.com/api/forms',
dataUrl: 'https://api.monprojet.com',
}),
]Stockage API REST — API sécurisée (Bearer token) (v1.3.0)
Si votre API exige un token JWT, ajoutez auth et enregistrez l'intercepteur fourni :
// app.config.ts
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { ngxFormsAuthInterceptor, provideNgxForms } from '@suntelecoms/ngx-dynamic-form';
providers: [
provideHttpClient(withInterceptors([ngxFormsAuthInterceptor])),
provideNgxForms({
storage: 'http',
apiUrl: 'https://api.monprojet.com/api/forms',
dataUrl: 'https://api.monprojet.com',
auth: {
loginUrl: 'https://api.monprojet.com/security-service/api/login',
username: 'admin',
password: 'secret',
},
}),
]L'intercepteur est no-op si
authn'est pas configuré. Les APIs ouvertes ne nécessitent aucune modification.Le token est mis en cache en mémoire et renouvelé automatiquement avant expiration (
expiresIn - 10s).
Options de configuration
| Option | Type | Description |
|---|---|---|
| storage | 'local' \| 'http' | Moteur de stockage des schémas |
| apiUrl | string | (mode http uniquement) URL de base de l'API de stockage |
| dataUrl | string | (optionnel) Base URL pour le chargement des options de champs |
| auth.loginUrl | string | (optionnel) Endpoint de login retournant { data: { accessToken, expiresIn } } |
| auth.username | string | Identifiant pour le login |
| auth.password | string | Mot de passe pour le login |
Endpoints REST attendus
L'API doit exposer les endpoints suivants :
GET /api/forms → SavedFormSchema[]
GET /api/forms/:id → SavedFormSchema
GET /api/forms/code/:code → SavedFormSchema (récupération par code unique)
POST /api/forms → SavedFormSchema (création — l'API génère l'id UUID)
PUT /api/forms/:id → SavedFormSchema (mise à jour)
DELETE /api/forms/:id → voidFormConfigService — gestion des schémas
FormConfigService est fourni par provideNgxForms(). Il gère les schémas de formulaires avec un Signal réactif et une API Observable.
Depuis la v1.2.9, chaque schéma possède un code unique saisi par l'utilisateur (ex : "contact-form") et une date de création createdAt. La sauvegarde est séparée en create() (nouveau formulaire) et update() (formulaire existant).
import { inject } from '@angular/core';
import { FormConfigService } from '@suntelecoms/ngx-dynamic-form';
export class MonComposant {
private formService = inject(FormConfigService);
// Signal réactif — liste mise à jour automatiquement
readonly schemas = this.formService.schemas; // Signal<SavedFormSchema[]>
// Lecture synchrone par id
getById(id: string) {
return this.formService.get(id); // SavedFormSchema | null
}
// Lecture synchrone par code
getByCode(code: string) {
return this.formService.getByCode(code); // SavedFormSchema | null
}
// Création (nouveau formulaire — l'id sera généré automatiquement)
createForm(code: string, title: string, config: AnyFormConfig) {
this.formService.create(code, title, config).subscribe(schema => {
console.log('Créé avec id :', schema.id);
});
}
// Mise à jour (formulaire existant)
updateForm(schema: SavedFormSchema, title: string, config: AnyFormConfig) {
this.formService.update(schema, title, config).subscribe(updated => {
console.log('Mis à jour :', updated.updatedAt);
});
}
// Suppression
deleteForm(id: string) {
this.formService.delete(id).subscribe(() => console.log('Supprimé'));
}
}Interface SavedFormSchema
interface SavedFormSchema {
id: string; // UUID généré automatiquement (côté serveur ou localStorage)
code: string; // Code unique saisi par l'utilisateur (ex : "contact-form")
title: string; // Nom affiché du formulaire
config: AnyFormConfig; // DynamicFormConfig OU StepperFormConfig
createdAt: string; // ISO 8601 — date de création (non modifiable)
updatedAt: string; // ISO 8601 — date de dernière modification
}
// AnyFormConfig = DynamicFormConfig | StepperFormConfig
// Pour distinguer les deux : 'steps' in config → c'est un StepperFormConfigStockage — LocalStorage vs API REST
Le plugin utilise une abstraction FormStorageAdapter qui permet de changer le backend de stockage sans toucher au reste de l'application.
Résumé des adaptateurs
| Option | Classe | Quand utiliser |
|---|---|---|
| storage: 'local' | LocalStorageAdapter | Développement, démo, usage hors-ligne |
| storage: 'http' | HttpStorageAdapter | Production avec backend Spring Boot |
Clé localStorage
Quand storage: 'local', les données sont stockées sous :
localStorage['ngx-dynamic-form__schemas'] = [
{ id, code, title, config, createdAt, updatedAt },
...
]Implémenter son propre adaptateur
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { FormStorageAdapter, SavedFormSchema } from '@suntelecoms/ngx-dynamic-form';
@Injectable()
export class IndexedDBAdapter extends FormStorageAdapter {
list(): Observable<SavedFormSchema[]> { /* ... */ }
get(id: string): Observable<SavedFormSchema | null> { /* ... */ }
getByCode(code: string): Observable<SavedFormSchema | null> { /* ... */ }
create(schema: Omit<SavedFormSchema, 'id'>): Observable<SavedFormSchema> { /* ... */ }
update(schema: SavedFormSchema): Observable<SavedFormSchema> { /* ... */ }
delete(id: string): Observable<void> { /* ... */ }
}
// app.config.ts
providers: [
{ provide: FormStorageAdapter, useClass: IndexedDBAdapter },
FormConfigService,
]Form Builder — éditeur visuel
Le Form Builder est une interface 3 panneaux intégrée dans le plugin. Il supporte deux modes : Formulaire (simple) et Stepper (multi-étapes).
Ajouter la route
{
path: 'builder',
loadComponent: () => import('@suntelecoms/ngx-dynamic-form').then(m => m.FormBuilderComponent),
}Interface
┌──────────────────┬───────────────────────────────┬─────────────────────┐
│ Palette │ [Formulaire] [Stepper] │ Propriétés │
│ │ Schéma / Aperçu │ (champ sélect.) │
│ ÉTAPES (stepper)│ │ │
│ ① Étape 1 ✏X│ ┌────────────────────────┐ │ Label : Nom │
│ ② Étape 2 ✏X│ │ Nom (text) ↑↓⎘🗑 │ │ Col : 6/12 │
│ + Ajouter │ │ Email (email) ↑↓⎘🗑 │ │ Icône : person │
│ │ └────────────────────────┘ │ Required : ✓ │
│ TYPES DE CHAMPS │ │ │
│ [Texte] │ Paramètres du formulaire │ Validation │
│ [E-mail] │ Code : [contact-form] │ Options │
│ [Select] ... │ Titre : [Mon formulaire] │ │
│ │ Bouton Envoyer : [Envoyer] │ │
│ Formulaires │ [ ] Réinitialiser │ │
│ sauvegardés │ [✓] Récapitulatif (stepper) │ │
│ │ │ │
│ │ [Schéma] [Aperçu] │ │
└──────────────────┴───────────────────────────────┴─────────────────────┘Champ Code
La topbar du Builder expose un champ Code à côté du titre. Ce code :
- Est saisi librement par l'utilisateur (ex :
"contact-form","inscription-2024") - Doit être unique — il est utilisé dans l'URL de partage (
/form/contact-form) et pour la récupération pargetByCode() - Est verrouillé après la première sauvegarde (le code ne peut plus changer une fois l'identifiant attribué)
Fonctionnalités du Builder
| Action | Description |
|---|---|
| Ajouter un champ | Clic sur un type dans la palette |
| Réordonner | Boutons ↑ ↓ sur chaque champ |
| Dupliquer | Copie d'un champ avec toutes ses propriétés |
| Supprimer | Icône corbeille |
| Éditer | Panneau droit — label, col, icône, validation, options… |
| Mode Stepper | Bascule Formulaire ↔ Stepper en haut de page |
| Gérer les étapes | Créer, renommer, supprimer les étapes |
| Récapitulatif | Case à cocher pour activer l'étape de synthèse |
| Aperçu | Onglet Aperçu — rendu réel en temps réel |
| Sauvegarder | Crée ou met à jour selon l'état courant (nouveau / existant) |
| Export JSON | JSON de la config dans un modal copiable |
| Export TypeScript | const config: DynamicFormConfig = { ... } ou StepperFormConfig |
| Partager | Copie l'URL /form/<code> dans le presse-papiers |
Input du FormBuilderComponent
| Input | Type | Défaut | Description |
|---|---|---|---|
| [backRoute] | string | '/' | Route du lien "Retour" dans la topbar |
Composants de navigation
MesFormulairesComponent — <ngx-mes-formulaires>
Liste et aperçu de tous les formulaires sauvegardés (simples et stepper). Affiche le code de chaque formulaire et permet de l'ouvrir directement via son URL /form/<code>.
{
path: 'mes-formulaires',
loadComponent: () => import('@suntelecoms/ngx-dynamic-form').then(m => m.MesFormulairesComponent),
}| Input | Type | Défaut | Description |
|---|---|---|---|
| [backRoute] | string | '/' | Lien "Retour" |
| [builderRoute] | string | '/builder' | Route vers le Builder |
| [formRoute] | string | '/form' | Préfixe de la route de rendu (+ /:code) |
FormLoaderComponent — <ngx-form-loader>
Charge et affiche un formulaire depuis son code unique — détecte automatiquement s'il s'agit d'un formulaire simple ou d'un stepper.
{
path: 'form/:code',
loadComponent: () => import('@suntelecoms/ngx-dynamic-form').then(m => m.FormLoaderComponent),
}| Input | Type | Défaut | Description |
|---|---|---|---|
| [formCode] | string | — | Code direct (sans routing, optionnel) |
| [backRoute] | string | '/' | Lien "Retour" |
| [builderRoute] | string | '/builder' | Lien "Éditer" |
Intégration dans un composant
Approche A — formulaire simple chargé par code
import { Component, computed, inject, signal } from '@angular/core';
import { DynamicFormComponent, FormConfigService } from '@suntelecoms/ngx-dynamic-form';
import type { DynamicFormConfig, DynamicFormSubmitEvent } from '@suntelecoms/ngx-dynamic-form';
@Component({
standalone: true,
imports: [DynamicFormComponent],
template: `
@if (activeConfig(); as cfg) {
<ngx-dynamic-form [config]="cfg" (formSubmit)="onSubmit($event)" />
}
`,
})
export class MonFormulaireComponent {
private formService = inject(FormConfigService);
selectedCode = signal('contact-form');
activeConfig = computed(() =>
this.formService.getByCode(this.selectedCode())?.config as DynamicFormConfig ?? null
);
onSubmit(event: DynamicFormSubmitEvent): void { console.log(event.value); }
}Approche B — stepper chargé par code (détection automatique)
import { Component, computed, inject, signal } from '@angular/core';
import { DynamicFormComponent, StepperFormComponent, FormConfigService } from '@suntelecoms/ngx-dynamic-form';
import type { DynamicFormConfig, StepperFormConfig, DynamicFormSubmitEvent } from '@suntelecoms/ngx-dynamic-form';
@Component({
standalone: true,
imports: [DynamicFormComponent, StepperFormComponent],
template: `
@if (flatConfig(); as flatCfg) {
<ngx-dynamic-form [config]="flatCfg" (formSubmit)="onSubmit($event)" />
}
@if (stepperConfig(); as stepperCfg) {
<ngx-stepper-form [config]="stepperCfg" (formSubmit)="onSubmit($event)" />
}
`,
})
export class MonFormulaireComponent {
private formService = inject(FormConfigService);
selectedCode = signal('inscription-2024');
private anyConfig = computed(() => this.formService.getByCode(this.selectedCode())?.config ?? null);
flatConfig = computed<DynamicFormConfig | null>(() => {
const c = this.anyConfig();
return c && !('steps' in c) ? c as DynamicFormConfig : null;
});
stepperConfig = computed<StepperFormConfig | null>(() => {
const c = this.anyConfig();
return c && 'steps' in c ? c as StepperFormConfig : null;
});
onSubmit(event: DynamicFormSubmitEvent): void { console.log(event.value); }
}Approche C — configuration TypeScript statique
import { Component } from '@angular/core';
import { DynamicFormComponent } from '@suntelecoms/ngx-dynamic-form';
import type { DynamicFormConfig, DynamicFormSubmitEvent } from '@suntelecoms/ngx-dynamic-form';
@Component({
standalone: true,
imports: [DynamicFormComponent],
template: `<ngx-dynamic-form [config]="config" (formSubmit)="onSubmit($event)" />`,
})
export class ContactComponent {
config: DynamicFormConfig = {
submitLabel: 'Envoyer',
fields: [
{ key: 'nom', type: 'text', label: 'Nom', col: 6, validation: { required: true } },
{ key: 'email', type: 'email', label: 'E-mail', col: 6, validation: { required: true } },
],
};
onSubmit(event: DynamicFormSubmitEvent): void { console.log(event.value); }
}Astuce : créez le formulaire dans le Builder (
/builder) → cliquez Export TypeScript → collez dans votre composant.
Approche D — avec backend API REST (Spring Boot)
Configurez provideNgxForms({ storage: 'http', apiUrl: '...' }) — tout le reste est identique.
// app.config.ts
provideNgxForms({
storage: 'http',
apiUrl: 'https://api.monprojet.com/api/forms',
}),
provideHttpClient(),// Exemple Spring Boot — FormSchemaController.java
@RestController
@RequestMapping("/api/forms")
public class FormSchemaController {
@GetMapping List<FormSchema> findAll() { ... }
@GetMapping("/{id}") FormSchema findById(@PathVariable String id) { ... }
@GetMapping("/code/{code}") FormSchema findByCode(@PathVariable String code) { ... }
@PostMapping ResponseEntity<FormSchema> create(@RequestBody FormSchema s) { ... }
@PutMapping("/{id}") FormSchema update(@PathVariable String id,
@RequestBody FormSchema s) { ... }
@DeleteMapping("/{id}") ResponseEntity<Void> delete(@PathVariable String id) { ... }
}L'id est généré par le serveur (
UUID.randomUUID()) lors duPOST. Le client envoie uniquement lecode(choisi par l'utilisateur), letitleet leconfig.
Pages de démonstration
L'application de démonstration (ng serve) illustre le plugin sur plusieurs cas d'usage :
| Route | Description |
|---|---|
| / | Page d'accueil |
| /inscription | Formulaire multi-sections avec showWhen |
| /contact | Formulaire de contact (select, textarea, radio) |
| /produit | Fiche produit — modes vue / édition / création |
| /builder | Form Builder — modes Formulaire et Stepper |
| /mes-formulaires | Gestionnaire — liste et aperçu de tous les formulaires |
| /form/:code | Rendu par code — formulaire simple ou stepper automatiquement |
Exemples complets
Formulaire d'inscription avec showWhen
config: DynamicFormConfig = {
submitLabel: 'Créer mon compte',
showReset: true,
fields: [
{ key: '_info', type: 'heading', text: 'Informations', level: 3, col: 12 },
{ key: 'firstName', type: 'text', label: 'Prénom', col: 6, icon: 'person',
validation: { required: true } },
{ key: 'lastName', type: 'text', label: 'Nom', col: 6, icon: 'badge',
validation: { required: true } },
{ key: 'email', type: 'email', label: 'E-mail', col: 12, icon: 'email',
validation: { required: true, email: true } },
{ key: '_div', type: 'divider', col: 12 },
{ key: 'accountType', type: 'radio', label: 'Type de compte', col: 12,
defaultValue: 'personal',
options: [
{ label: 'Particulier', value: 'personal' },
{ label: 'Professionnel', value: 'pro' },
] },
{ key: 'company', type: 'text', label: 'Entreprise', col: 6, icon: 'business',
showWhen: { field: 'accountType', operator: 'equals', value: 'pro' } },
{ key: 'siret', type: 'text', label: 'SIRET', col: 6, icon: 'tag',
showWhen: { field: 'accountType', operator: 'equals', value: 'pro' },
validation: { minLength: 14, maxLength: 14 } },
{ key: 'terms', type: 'checkbox',
placeholder: "J'accepte les CGU *", col: 12,
validation: { required: true, messages: { required: 'Vous devez accepter les CGU.' } } },
],
};Formulaire stepper — processus d'inscription en 3 étapes
config: StepperFormConfig = {
showSummary: true,
submitLabel: 'Créer mon compte',
steps: [
{
label: 'Identité',
icon: 'person',
fields: [
{ key: 'firstName', type: 'text', label: 'Prénom', col: 6, validation: { required: true } },
{ key: 'lastName', type: 'text', label: 'Nom', col: 6, validation: { required: true } },
{ key: 'birthDate', type: 'date', label: 'Date de naissance', col: 6 },
{ key: 'gender', type: 'select', label: 'Genre', col: 6,
options: [{ label: 'Homme', value: 'M' }, { label: 'Femme', value: 'F' }] },
],
},
{
label: 'Coordonnées',
icon: 'contact_mail',
fields: [
{ key: 'email', type: 'email', label: 'E-mail', col: 12,
validation: { required: true, email: true } },
{ key: 'phone', type: 'tel', label: 'Téléphone', col: 6 },
{ key: 'city', type: 'text', label: 'Ville', col: 6 },
],
},
{
label: 'Sécurité',
icon: 'lock',
fields: [
{ key: 'password', type: 'password', label: 'Mot de passe', col: 6,
validation: { required: true, custom: v => v?.length < 8 ? 'Minimum 8 caractères' : null } },
{ key: 'terms', type: 'checkbox', placeholder: "J'accepte les CGU", col: 12,
validation: { required: true } },
],
},
],
};Fiche produit avec préremplissage et mode vue/édition
mode = signal<'view' | 'edit'>('edit');
prefill = { name: 'Casque Pro X1', price: 129.99, stock: 42 };
get config(): DynamicFormConfig {
const ro = this.mode() === 'view';
return {
submitLabel: 'Sauvegarder',
fields: [
{ key: 'name', type: 'text', label: 'Nom', col: 8, icon: 'inventory', readonly: ro,
validation: { required: true } },
{ key: 'price', type: 'number', label: 'Prix', col: 4, icon: 'euro', readonly: ro },
{ key: 'stock', type: 'number', label: 'Stock', col: 4, icon: 'inventory_2', readonly: ro },
],
};
}<ngx-dynamic-form
[config]="config"
[initialValues]="prefill"
(formSubmit)="onSubmit($event)"
/>Personnalisation CSS
/* styles.css */
:root {
--ngx-form-gap: 1.25rem;
--ngx-color-border: #e2e8f0;
--ngx-color-text: #1a202c;
--ngx-color-hint: #718096;
}Pour les couleurs des champs Material (outline, focus, erreur), personnalisez le thème Angular Material selon la documentation officielle.
Interfaces TypeScript
// Exports publics de @suntelecoms/ngx-dynamic-form
// Composants
export { DynamicFormComponent } // <ngx-dynamic-form>
export { StepperFormComponent } // <ngx-stepper-form> ← v1.2.0
export { FormBuilderComponent } // <ngx-form-builder>
export { MesFormulairesComponent } // <ngx-mes-formulaires>
export { FormLoaderComponent } // <ngx-form-loader>
// Services & Providers
export { FormConfigService } // inject(FormConfigService)
export { FormStorageAdapter } // classe abstraite de stockage
export { LocalStorageAdapter } // implémentation localStorage
export { HttpStorageAdapter } // implémentation API REST
export { FORM_API_URL } // InjectionToken<string>
export { provideNgxForms } // function(options) → EnvironmentProviders
export { NGX_DATA_API_URL } // InjectionToken<string> — base URL pour les options dynamiques ← v1.2.5
export { ngxFormsAuthInterceptor } // HttpInterceptorFn — ajoute Authorization: Bearer ← v1.3.0
export { NgxFormsAuthService } // service token/cache ← v1.3.0
export { NGX_FORMS_AUTH } // InjectionToken<NgxFormsAuthOptions> ← v1.3.0
// Types
export type { NgxFormsOptions } // { storage: 'local' } | { storage: 'http', apiUrl: string }
export type { NgxFormsAuthOptions } // { loginUrl, username, password } ← v1.3.0
export type { AnyFormConfig } // DynamicFormConfig | StepperFormConfig ← v1.2.0
export type { SavedFormSchema } // { id, code, title, config, createdAt, updatedAt } ← v1.2.9
export type { DynamicFormConfig }
export type { DynamicFormField }
export type { DynamicFormSubmitEvent }
export type { StepperFormConfig } // ← v1.2.0
export type { FormStep } // ← v1.2.0
export type { FieldType }
export type { FieldValidation }
export type { FieldCondition }
export type { SelectOption }Changelog
v1.3.1
- Fix boucle infinie auth :
NgxFormsAuthServiceutilise désormaisHttpBackendau lieu deHttpClientpour la requête de login — l'intercepteurngxFormsAuthInterceptorn'intercepte plus la requête de login elle-même, évitant une récursion infinie qui empêchait le chargement des formulaires
v1.3.0
- Authentification Bearer token : nouvelle option
authdansprovideNgxForms({ auth: { loginUrl, username, password } }) ngxFormsAuthInterceptor: intercepteur HTTP fonctionnel à ajouter dansprovideHttpClient(withInterceptors([ngxFormsAuthInterceptor]))— injecte automatiquement le headerAuthorization: Bearer <token>sur toutes les requêtesNgxFormsAuthService: service de gestion du token JWT — login automatique au premier appel, cache en mémoire, renouvellement automatique 10 secondes avant expiration- No-op sans auth : l'intercepteur est transparent si
authn'est pas configuré — aucune modification requise pour les APIs ouvertes - Compatibilité : fonctionne avec les modes
storage: 'local'etstorage: 'http'
v1.2.9
- Code unique par formulaire : nouvelle propriété
codedansSavedFormSchema— identifiant court saisi par l'utilisateur (ex :"contact-form"), unique dans le stockage - Date de création : nouvelle propriété
createdAtdansSavedFormSchema— ISO 8601, non modifiable après création - API de sauvegarde séparée :
FormConfigService.create(code, title, config)pour les nouveaux formulaires,update(schema, title, config)pour les modifications — remplace l'ancienne méthodesave() - Récupération par code :
FormConfigService.getByCode(code)etFormStorageAdapter.getByCode(code)— permet de charger un formulaire depuis son code sans connaître son id - Routage par code :
FormLoaderComponentlit désormais le paramètre de route:code(était:id) et appellegetByCode()— l'inputformCoderemplaceformId - MesFormulairesComponent : les liens vers
/form/:codeutilisent maintenantschema.code— affiche "Code :" à la place de "ID :" dans la liste - Form Builder : champ Code dans la topbar, verrouillé après la première sauvegarde —
copyFormUrl()génère/form/<code>au lieu de/form/<id> - Endpoint REST :
GET /api/forms/code/:codeajouté ;POST /api/formspour la création (id UUID auto-généré côté serveur) ;PUT /api/forms/:idpour la mise à jour seule
v1.2.8
- 6 nouveaux types de champs :
switch(mat-slide-toggle),range(mat-slider),color(color picker + affichage hex),location(géolocalisation GPS),time,month,week,datetime-localaccessibles depuis le Builder - Datepicker Material : le type
dateutilise désormaismat-datepicker(calendrier popup Material) — conversion string → Date automatique pour les valeurs préremplies - Géolocalisation : le type
locationembarque un bouton GPS qui déclenchenavigator.geolocation.getCurrentPosition()et remplit le champ avec"lat,lng" - Curseur range : min, max, step configurables depuis le Builder — affichage de la valeur courante en temps réel
- Couleurs bordure et label : nouvelles propriétés
borderColoretlabelColorsurDynamicFormField— appliquées via CSS custom properties, ciblent les composants Material (MDC) via:host[style*="..."]
v1.2.7
- Apparence
simple: 3ème variante — label fixe au-dessus, input borduré avecborder-radius: 8px— sansmat-form-field. Disponible globalement ou par champ - Masque de saisie : propriété
mask—#= chiffre, autres chars = séparateurs. Présets dans le Builder (téléphone SN, international, date, heure, CB, code postal) - Préfixe / suffixe texte : propriétés
prefixetsuffix— affichés à l'intérieur du champ - Styling complet par champ :
fontSize,textAlign,color,backgroundColor,fontWeight,fontStyle,cssClass,styles(CSS inline arbitraire) - Apparence globale configurable :
DynamicFormConfig.appearanceet.floatLabel— le Builder expose ces réglages dans "Paramètres du formulaire"
v1.2.6
- Fix : en mode édition, les champs
select/multiselect/radioavecurlreçoivent correctement leur valeur initiale même si l'API retourne un objet imbriqué - Fix appliqué aussi dans
StepperFormComponent
v1.2.5
- Options dynamiques depuis une API pour les champs
select,multiselect,radio: propriétésurl,displayName,displayValue - Token
NGX_DATA_API_URLet optiondataUrldansprovideNgxForms() - Form Builder : toggle Statiques / Depuis une API dans le panneau Options
- Spinner de chargement dans le dropdown pendant le fetch des options
v1.2.0
- Ajout du composant
<ngx-stepper-form>pour les formulaires multi-étapes - Étape Récapitulatif automatique (
showSummary: true) avec boutons Modifier par étape - Mode Stepper dans le Form Builder
AnyFormConfig=DynamicFormConfig | StepperFormConfigMesFormulairesComponentetFormLoaderComponentdétectent et rendent les deux types
v1.1.0
- Grille 12 colonnes (
col) - Form Builder, MesFormulaires et FormLoader intégrés dans le plugin
- Système de stockage abstrait (localStorage / API REST)
FormConfigServiceSignal-based
Auteur
Amadou Diallo — @amepro 📧 [email protected]
Développé dans le cadre du projet SUNTELECOMS — Plugin Angular 19 standalone.
MIT License — Libre d'utilisation, de modification et de distribution.
