Dans ce guide, nous expliquerons la création d'un formulaire de commentaires utilisateur dynamique à l'aide de la bibliothèque @perseid/form, une alternative puissante à Formik et React Hook Form. Vous verrez comment @perseid/form facilite la gestion de l'état, de la validation et du rendu conditionnel du formulaire. Le formulaire que nous allons créer demandera aux utilisateurs d'évaluer un service et de fournir des commentaires. En fonction de la note, il affichera un message de « remerciement » ou invitera l'utilisateur à fournir des commentaires supplémentaires.
? Commençons !
La première étape consiste à définir la configuration du formulaire. Cette configuration décrit le comportement du formulaire, y compris les champs, les étapes et le flux entre eux. Ici, nous allons créer des champs pour une note et un avis, avec une logique conditionnelle basée sur la note de l'utilisateur. Nous définirons également des messages pour les commentaires positifs et négatifs.
Voici le code de configuration :
import { type Configuration } from "@perseid/form"; const formConfiguration: Configuration = { // Root step-the form will start from there. root: "feedback", // Callback triggered on form submission. onSubmit(data) { alert(`Submitting the following JSON: ${JSON.stringify(data)}`); return Promise.resolve(); }, // `fields` define the data model the form is going to deal with. // Expect the submitted data JSON to match this schema. fields: { rating: { type: "integer", required: true, }, review: { type: "string", required: true, // Display this field only if condition is met... condition: (inputs) => inputs.rating !== null && (inputs.rating as number) < 3, }, // Type `null` means that the value of this field will not be included in submitted data. submit: { type: "null", submit: true, }, message_good: { type: "null", }, message_bad: { type: "null", }, }, // Now that fields are defined, you can organize them in a single or multiple steps, // depending on the UI you want to build! steps: { feedback: { fields: ["rating", "review", "submit"], // Whether to submit the form at the end of this step. submit: true, // Next step is conditionned to previous user inputs... nextStep: (inputs) => (inputs.rating as number) < 3 ? "thanks_bad" : "thanks_good", }, thanks_good: { fields: ["message_good"], }, thanks_bad: { fields: ["message_bad"], }, }, };
Dans cette configuration :
Le point clé à comprendre ici est la fonction de la propriété field. Il définit la structure des données qui seront soumises, agissant essentiellement comme un modèle de données. En revanche, la propriété steps décrit le flux du formulaire, déterminant comment ces champs seront présentés à l'utilisateur.
Maintenant que nous avons la configuration, il est temps de créer l'interface utilisateur réelle qui restituera le formulaire. En utilisant @perseid/form/react, nous pouvons créer des composants de champ personnalisés pour gérer les interactions des utilisateurs pour chaque partie du formulaire.
Voici le composant principal de React :
import React from "react"; import Form, { type FormFieldProps } from "@perseid/form/react"; // The actual React component, used to build the UI! function Field(props: FormFieldProps): JSX.Element { const { path, engine, value, status } = props; const [currentRating, setCurrentRating] = React.useState(0); // Display a different element depending on the field... if (path === "thanks_good.1.message_good") { return ( <div className="message"> <h1>Thanks for the feedback ?</h1> <p>We are glad you enjoyed!</p> </div> ); } if (path === "thanks_bad.1.message_bad") { return ( <div className="message"> <h1>We're sorry to hear that ?</h1> <p>We'll do better next time, promise!</p> </div> ); } if (path === "feedback.0.rating") { return ( // Depending on the field status, define some extra classes for styling... <div className={`rating ${status === "error" ? "rating--error" : ""}`} onMouseLeave={() => { setCurrentRating((value as number | null) ?? 0); }} > <h1>How would you rate our service?</h1> {[1, 2, 3, 4, 5].map((rating) => ( <span key={rating} className={`rating__star ${ currentRating >= rating ? "rating__star--active" : "" }`} onMouseEnter={() => { setCurrentRating(rating); }} onClick={() => { // On click, notify the form engine about new user input. engine.userAction({ type: "input", path, data: rating }); }} ></span> ))} </div> ); } if (path === "feedback.0.review") { return ( <div className={`review ${status === "error" ? "review--error" : ""}`}> <label>Could you tell us more?</label> <textarea onChange={(e) => engine.userAction({ type: "input", path, data: e.target.value }) } /> </div> ); } // path === 'feedback.0.submit' return ( <button className="submit" onClick={() => { engine.userAction({ type: "input", path, data: true }); }} > Submit </button> ); }
Ici, le composant Field utilise la prop path pour décider quoi rendre :
Messages « Merci » qui apparaissent en fonction de la note. Le formulaire ajustera dynamiquement ses champs et ses étapes en fonction des saisies de l'utilisateur.
Plutôt cool, non ?
Maintenant que notre configuration de formulaire et notre composant sont prêts, intégrons-les dans une application React de base. Voici le code pour initialiser et restituer le formulaire :
import { createRoot, type Root } from "react-dom/client"; // Let's run the app! let app: Root; // Creating React root... const container = document.querySelector("#root") as unknown as HTMLElement; app = createRoot(container); app.render( // Router is the main component for any Perseid app. <Form Field={Field} configuration={formConfiguration} /> );
Ce code monte le formulaire sur le DOM à l'aide de l'API createRoot de React. Le composant Form, qui connecte notre configuration et notre composant Field, gère tout le reste.
Très bien, nous avons notre logique d'application, mais si vous exécutez le code maintenant, vous verrez que c'est un peu... brut ?
Alors, embellissons le formulaire en ajoutant quelques styles et animations ! Vous trouverez ci-dessous une feuille de style simple qui la rend beaucoup plus attrayante :
// A few animations for fun... @keyframes swipe-out { 0% { opacity: 1; transform: translateX(0); } 75% { opacity: 0; transform: translateX(-100%); } 100% { opacity: 0; transform: translateX(-100%); } } @keyframes swipe-in-one { 0% { opacity: 0; transform: translateX(100%); } 75% { transform: translateX(0); } 100% { opacity: 1; transform: translateX(0); } } @keyframes swipe-in-two { 0% { opacity: 0; transform: translateX(0); } 75% { transform: translateX(-100%); } 100% { opacity: 1; transform: translateX(-100%); } } @keyframes bubble-in { 0% { transform: scale(0.5); } 75% { transform: scale(1.5); } 100% { transform: scale(1); } } @keyframes fade-in { 0% { opacity: 0; } 100% { opacity: 1; } } // Some global basic styling... * { box-sizing: border-box; } body { margin: 0; display: grid; height: 100vh; color: #aaaaaa; align-items: center; font-family: "Helvetica", sans-serif; } // And form-specific styling. .perseid-form { width: 100%; margin: auto; &__steps { display: flex; overflow: hidden; } &__step { min-width: 100%; padding: 1rem 3rem; animation: 500ms ease-in-out forwards swipe-out; &__fields { display: grid; row-gap: 2rem; } } &__step[class*="active"]:first-child { animation: 500ms ease-in-out forwards swipe-in-one; } &__step[class*="active"]:last-child:not(:first-child) { animation: 500ms ease-in-out forwards swipe-in-two; } } .submit { border: none; cursor: pointer; padding: 1rem 2rem; border-radius: 8px; color: #fefefe; font-size: 1.25rem; background: #46c0b0; justify-self: flex-end; transition: all 250ms ease-in-out; &:hover { background: #4cccbb; } } .rating { position: relative; padding: 0.25rem 0; &__star { cursor: pointer; display: inline-block; font-size: 2rem; min-width: 2rem; min-height: 2rem; &::after { content: "⚪️"; } &--active { animation: 250ms ease-in-out forwards bubble-in; &::after { content: "?"; } } } &[class*="error"] { &::after { left: 0; bottom: -1.5rem; color: #f13232; position: absolute; font-size: 0.75rem; content: "? This field is required"; animation: 250ms ease-in-out forwards fade-in; } } } .review { display: grid; row-gap: 1rem; position: relative; animation: 250ms ease-in-out forwards fade-in; label { font-size: 1.25rem; } textarea { resize: none; min-height: 5rem; border-radius: 8px; border: 1px solid #46c0b0; transition: all 250ms ease-in-out; } &[class*="error"] { &::after { left: 0; bottom: -1.5rem; color: #f13232; position: absolute; font-size: 0.75rem; content: "? This field is required"; animation: 250ms ease-in-out forwards fade-in; } } } @media screen and (min-width: 30rem) { .perseid-form { max-width: 30rem; } }
Et voilà ?
Félicitations ! ? Vous venez de créer un formulaire de commentaires utilisateur dynamique avec Perseid et React.
Dans ce tutoriel, nous avons expliqué comment :
N'hésitez pas à expérimenter des champs et des étapes supplémentaires en fonction de votre cas d'utilisation. Amusez-vous à créer des formulaires géniaux ! ?
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!