SmarMaker - Documentation
Docs» 15_training:module6-smartcommon-composants:formulaires

Chapitre 3 : Formulaires

Form

Le composant Form gère la soumission et la validation des formulaires.

Syntaxe de base

snippet.javascript
import { Form, Input, Button } from '@cap-rel/smartcommon';
 
function ContactForm() {
    const handleSubmit = (values) => {
        console.log(values);
        // { name: 'Jean', email: 'jean@example.com' }
    };
 
    return (
        <Form onSubmit={handleSubmit}>
            <Input name="name" label="Nom" required />
            <Input name="email" label="Email" type="email" required />
            <Button type="submit">Envoyer</Button>
        </Form>
    );
}

Avec valeurs initiales

snippet.javascript
function EditProductForm({ product }) {
    const handleSubmit = async (values) => {
        await api.private.put(`products/${product.id}`, { json: values });
    };
 
    return (
        <Form
            onSubmit={handleSubmit}
            defaultValues={{
                label: product.label,
                price: product.price,
                description: product.description
            }}
        >
            <Input name="label" label="Nom du produit" />
            <Input name="price" label="Prix" type="number" />
            <Input name="description" label="Description" />
            <Button type="submit">Enregistrer</Button>
        </Form>
    );
}

Input

Champ de saisie texte polyvalent.

Props principales

Prop Type Description
name string Nom du champ (obligatoire)
label string Libellé affiché
type string 'text', 'email', 'password', 'number', 'tel'
placeholder string Texte indicatif
required boolean Champ obligatoire
disabled boolean Champ désactivé
error string Message d'erreur

Exemples

snippet.javascript
// Texte simple
<Input name="firstName" label="Prénom" />
 
// Email avec validation
<Input name="email" label="Email" type="email" required />
 
// Mot de passe
<Input name="password" label="Mot de passe" type="password" />
 
// Nombre
<Input name="quantity" label="Quantité" type="number" min={0} max={100} />
 
// Téléphone
<Input name="phone" label="Téléphone" type="tel" />
 
// Avec placeholder
<Input
    name="search"
    placeholder="Rechercher..."
    type="text"
/>
 
// Désactivé
<Input name="ref" label="Référence" disabled value="PRD-001" />

Textarea

Zone de texte multiligne.

snippet.javascript
import { Textarea } from '@cap-rel/smartcommon';
 
<Textarea
    name="description"
    label="Description"
    rows={4}
    placeholder="Décrivez le produit..."
/>

Select

Liste déroulante.

snippet.javascript
import { Select } from '@cap-rel/smartcommon';
 
// Options simples
<Select
    name="category"
    label="Catégorie"
    options={[
        { value: 'electronics', label: 'Électronique' },
        { value: 'clothing', label: 'Vêtements' },
        { value: 'food', label: 'Alimentation' }
    ]}
/>
 
// Avec valeur vide
<Select
    name="status"
    label="Statut"
    placeholder="Sélectionnez..."
    options={[
        { value: 'draft', label: 'Brouillon' },
        { value: 'published', label: 'Publié' },
        { value: 'archived', label: 'Archivé' }
    ]}
/>
 
// Options depuis une API
function ProductForm() {
    const [categories, setCategories] = useState([]);
 
    useEffect(() => {
        api.private.get('categories').json().then(data => {
            setCategories(data.map(c => ({
                value: c.id,
                label: c.name
            })));
        });
    }, []);
 
    return (
        <Form>
            <Select
                name="category_id"
                label="Catégorie"
                options={categories}
            />
        </Form>
    );
}

Boolean

Interrupteur on/off.

snippet.javascript
import { Boolean } from '@cap-rel/smartcommon';
 
<Boolean
    name="isActive"
    label="Produit actif"
/>
 
<Boolean
    name="notifications"
    label="Recevoir les notifications"
    defaultValue={true}
/>

Checker

Case à cocher.

snippet.javascript
import { Checker } from '@cap-rel/smartcommon';
 
<Checker
    name="acceptTerms"
    label="J'accepte les conditions générales"
    required
/>
 
// Groupe de cases
<div>
    <Checker name="options.express" label="Livraison express" />
    <Checker name="options.gift" label="Emballage cadeau" />
    <Checker name="options.insurance" label="Assurance" />
</div>

Calendar

Sélecteur de date.

snippet.javascript
import { Calendar } from '@cap-rel/smartcommon';
 
// Date simple
<Calendar
    name="birthdate"
    label="Date de naissance"
/>
 
// Avec contraintes
<Calendar
    name="startDate"
    label="Date de début"
    minDate={new Date()}
    maxDate={new Date(2025, 11, 31)}
/>
 
// Date et heure
<Calendar
    name="appointment"
    label="Rendez-vous"
    showTime={true}
/>

Timer

Sélecteur d'heure.

snippet.javascript
import { Timer } from '@cap-rel/smartcommon';
 
<Timer
    name="startTime"
    label="Heure de début"
/>
 
<Timer
    name="duration"
    label="Durée"
    format="HH:mm"
/>

Range

Slider de valeur.

snippet.javascript
import { Range } from '@cap-rel/smartcommon';
 
<Range
    name="price"
    label="Prix maximum"
    min={0}
    max={1000}
    step={10}
/>
 
<Range
    name="rating"
    label="Note minimale"
    min={1}
    max={5}
    step={0.5}
/>

ColorPicker

Sélecteur de couleur.

snippet.javascript
import { ColorPicker } from '@cap-rel/smartcommon';
 
<ColorPicker
    name="color"
    label="Couleur du produit"
/>

FilesUploader

Upload de fichiers.

snippet.javascript
import { FilesUploader } from '@cap-rel/smartcommon';
 
<FilesUploader
    name="documents"
    label="Documents"
    accept=".pdf,.doc,.docx"
    multiple
    maxFiles={5}
/>

PhotosUploader

Upload de photos avec prévisualisation.

snippet.javascript
import { PhotosUploader } from '@cap-rel/smartcommon';
 
<PhotosUploader
    name="photos"
    label="Photos du produit"
    maxFiles={10}
    maxSize={5 * 1024 * 1024}  // 5 Mo
/>

SignaturePad

Zone de signature manuscrite.

snippet.javascript
import { SignaturePad } from '@cap-rel/smartcommon';
 
<SignaturePad
    name="signature"
    label="Signature"
    width={400}
    height={200}
/>

Exemple complet : Formulaire produit

snippet.javascript
import { useEffect } from 'react';
import {
    Page,
    Block,
    Form,
    Input,
    Textarea,
    Select,
    Boolean,
    Calendar,
    PhotosUploader,
    Button
} from '@cap-rel/smartcommon';
import { useApi, useNavigation, useStates } from '@cap-rel/smartcommon';
 
export const ProductFormPage = ({ productId }) => {
    const api = useApi();
    const navigate = useNavigation();
    const isEdit = !!productId;
 
    const st = useStates({
        initialStates: {
            product: null,
            categories: [],
            loading: isEdit,
            submitting: false
        }
    });
 
    // Charger les catégories
    useEffect(() => {
        api.private.get('categories').json().then(data => {
            st.set('categories', data.map(c => ({
                value: c.id,
                label: c.name
            })));
        });
    }, []);
 
    // Charger le produit si édition
    useEffect(() => {
        if (isEdit) {
            api.private.get(`products/${productId}`).json().then(data => {
                st.set('product', data);
                st.set('loading', false);
            });
        }
    }, [productId]);
 
    const handleSubmit = async (values) => {
        st.set('submitting', true);
 
        try {
            if (isEdit) {
                await api.private.put(`products/${productId}`, { json: values });
            } else {
                await api.private.post('products', { json: values });
            }
            navigate('/products');
        } catch (err) {
            alert('Erreur : ' + err.message);
        } finally {
            st.set('submitting', false);
        }
    };
 
    if (st.get('loading')) {
        return <Page title="Chargement..."><Spinner /></Page>;
    }
 
    return (
        <Page title={isEdit ? 'Modifier le produit' : 'Nouveau produit'}>
            <Block>
                <Form
                    onSubmit={handleSubmit}
                    defaultValues={st.get('product') || {
                        label: '',
                        description: '',
                        price: 0,
                        category_id: null,
                        isActive: true,
                        availableFrom: null,
                        photos: []
                    }}
                >
                    {/* Informations de base */}
                    <Input
                        name="label"
                        label="Nom du produit"
                        required
                    />
 
                    <Textarea
                        name="description"
                        label="Description"
                        rows={4}
                    />
 
                    <Input
                        name="price"
                        label="Prix (€)"
                        type="number"
                        min={0}
                        step={0.01}
                        required
                    />
 
                    <Select
                        name="category_id"
                        label="Catégorie"
                        options={st.get('categories')}
                        required
                    />
 
                    {/* Options */}
                    <Boolean
                        name="isActive"
                        label="Produit actif"
                    />
 
                    <Calendar
                        name="availableFrom"
                        label="Disponible à partir de"
                    />
 
                    {/* Photos */}
                    <PhotosUploader
                        name="photos"
                        label="Photos"
                        maxFiles={5}
                    />
 
                    {/* Actions */}
                    <div className="flex gap-2 mt-4">
                        <Button
                            type="button"
                            variant="outline"
                            onClick={() => navigate(-1)}
                        >
                            Annuler
                        </Button>
                        <Button
                            type="submit"
                            loading={st.get('submitting')}
                        >
                            {isEdit ? 'Enregistrer' : 'Créer'}
                        </Button>
                    </div>
                </Form>
            </Block>
        </Page>
    );
};

Validation avec Zod

snippet.javascript
import { z } from 'zod';
import { Form, Input, Button } from '@cap-rel/smartcommon';
import { useForm } from '@cap-rel/smartcommon';
 
const schema = z.object({
    email: z.string().email('Email invalide'),
    password: z.string().min(8, 'Minimum 8 caractères'),
    confirmPassword: z.string()
}).refine(data => data.password === data.confirmPassword, {
    message: 'Les mots de passe ne correspondent pas',
    path: ['confirmPassword']
});
 
function RegisterForm() {
    const form = useForm({ schema });
 
    const handleSubmit = async (data) => {
        // data est validé
        console.log(data);
    };
 
    return (
        <Form form={form} onSubmit={handleSubmit}>
            <Input name="email" label="Email" type="email" />
            <Input name="password" label="Mot de passe" type="password" />
            <Input name="confirmPassword" label="Confirmer" type="password" />
            <Button type="submit">S'inscrire</Button>
        </Form>
    );
}

Points clés à retenir

  1. Form gère la soumission et collecte les valeurs
  2. name est obligatoire sur chaque champ
  3. defaultValues pour pré-remplir (édition)
  4. required pour les champs obligatoires
  5. Zod pour la validation avancée

← Chapitre précédent | Retour au module | Chapitre suivant : Affichage →

Previous Next

Made with ❤ by CAP-REL · SmartMaker · GNU AGPL v3+
Code source · Faire un don
SmarMaker - Documentation
Traductions de cette page:
  • Français
  • Deutsch
  • English
  • Español
  • Italiano
  • Nederlands

Table of Contents

Table des matières

  • Chapitre 3 : Formulaires
    • Form
      • Syntaxe de base
      • Avec valeurs initiales
    • Input
      • Props principales
      • Exemples
    • Textarea
    • Select
    • Boolean
    • Checker
    • Calendar
    • Timer
    • Range
    • ColorPicker
    • FilesUploader
    • PhotosUploader
    • SignaturePad
    • Exemple complet : Formulaire produit
    • Validation avec Zod
    • Points clés à retenir
  • SmartAuth
  • SmartMaker - Back (PHP)
    • Mapping Dolibarr - React
  • SmartMaker - Front (React)
    • Animations de pages
    • Architecture
    • Astuces
    • Calendar
    • Composants et pages
    • Configuration du Provider
    • Debug et Logs
    • Hooks SmartCommon
    • PWA (Progressive Web App)
    • Requêtes API
    • Routage
    • SmartCommon
    • Stockage de données
    • Thèmes
    • Traductions
  • HowTo - Pas à pas - Votre première application
    • Développement PHP (back)
    • Développement React (front)
    • Première étape : Module Builder Dolibarr
    • SmartAuth
    • SmartBoot : Un "squelette" quasiment prêt à l'emploi
  • Formation SmartMaker
    • Module 1 : Fondamentaux JavaScript ES6+
      • Chapitre 1 : Variables et Scope
      • Chapitre 2 : Fonctions
      • Chapitre 3 : Programmation Asynchrone
      • Chapitre 4 : Modules ES6
    • Module 2 : Introduction à React
      • Chapitre 1 : Philosophie React
      • Chapitre 2 : JSX
      • Chapitre 3 : Composants
    • Module 3 : Hooks React Fondamentaux
      • Chapitre 1 : useState
      • Chapitre 2 : useEffect
      • Chapitre 3 : useRef
      • Chapitre 4 : useContext
    • Module 4 : React Avancé
      • Chapitre 1 : useCallback et useMemo
      • Chapitre 2 : Custom Hooks
      • Chapitre 3 : Redux et Redux Toolkit
    • Module 5 : Architecture SmartMaker
      • Chapitre 1 : Structure du projet
      • Chapitre 2 : Configuration
      • Chapitre 3 : Flux de données
    • Module 6 : SmartCommon - Composants
      • Chapitre 1 : Mise en page
      • Chapitre 2 : Navigation
      • Chapitre 3 : Formulaires
      • Chapitre 4 : Affichage
    • Module 7 : SmartCommon - Hooks
      • Chapitre 1 : useApi
      • Chapitre 2 : Gestion d'état
      • Chapitre 3 : Hooks utilitaires
    • Module 8 : Backend API (PHP)
      • Chapitre 1 : Routage
      • Chapitre 2 : Controllers
      • Chapitre 3 : Mappers
      • Extrafields et formulaires dynamiques
    • Module 9 : Intégration complète
      • Chapitre 1 : Backend
      • Chapitre 2 : Frontend
      • Chapitre 3 : Déploiement
    • Module 10 : Fonctionnalités avancées
      • Chapitre 1 : Mode offline
      • Chapitre 2 : Internationalisation (i18n)
      • Chapitre 3 : Autres fonctionnalités
    • Module 11 : Bonnes pratiques
  • Démonstration
  • Start
  • Composants et pages
  • Afficher le texte source
  • Anciennes révisions
  • Liens de retour
  • Haut de page