Dalam panduan ini, kami akan meneruskan pembinaan borang maklum balas pengguna dinamik menggunakan pustaka @perseid/form, alternatif yang berkuasa untuk Formly dan Superforms. Anda akan melihat cara @perseid/form memudahkan untuk mengurus keadaan borang, pengesahan dan pemaparan bersyarat. Borang yang akan kami bina akan meminta pengguna menilai perkhidmatan dan memberikan maklum balas. Bergantung pada penilaian, ia sama ada akan memaparkan mesej "terima kasih" atau menggesa pengguna untuk memberikan maklum balas tambahan.
? Jom mulakan!
Langkah pertama ialah mentakrifkan konfigurasi borang. Konfigurasi ini menggariskan cara borang itu berkelakuan, termasuk medan, langkah dan aliran di antaranya. Di sini, kami akan mencipta medan untuk penilaian dan ulasan, dengan logik bersyarat berdasarkan penilaian pengguna. Kami juga akan menentukan mesej untuk maklum balas positif dan negatif.
Berikut ialah kod konfigurasi:
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"], }, }, };
Dalam konfigurasi ini:
Perkara utama yang perlu difahami di sini ialah fungsi sifat medan. Ia mentakrifkan struktur data yang akan diserahkan, pada asasnya bertindak sebagai model data. Sebaliknya, sifat langkah menggariskan aliran borang, menentukan cara medan ini akan dipersembahkan kepada pengguna.
Sekarang kita mempunyai konfigurasi, tiba masanya untuk membina UI sebenar yang akan menghasilkan borang. Menggunakan @perseid/form/svelte, kami boleh mencipta komponen medan tersuai untuk mengurus interaksi pengguna bagi setiap bahagian borang.
Berikut ialah komponen teras Svelte:
<!-- The actual Svelte component, used to build the UI! --> <script lang="ts" context="module"> import type { FormFieldProps } from "@perseid/form/svelte"; </script> <script lang="ts"> export let path: FormFieldProps['path']; export let type: FormFieldProps['type']; export let value: FormFieldProps['value']; export let Field: FormFieldProps['Field']; export let error: FormFieldProps['error']; export let status: FormFieldProps['status']; export let engine: FormFieldProps['engine']; export let fields: FormFieldProps['fields']; export let isActive: FormFieldProps['isActive']; export let activeStep: FormFieldProps['activeStep']; export let isRequired: FormFieldProps['isRequired']; export let setActiveStep: FormFieldProps['setActiveStep']; export let useSubscription: FormFieldProps['useSubscription']; let currentRating = 0; $: currentValue = value as number; $: fields, isActive, activeStep, setActiveStep, useSubscription, type, error, Field, isRequired; const setCurrentRating = (newRating: number) => { currentRating = newRating; }; const handleReviewChange = (event: Event) => { engine.userAction({ type: "input", path, data: (event.target as HTMLTextAreaElement).value }) }; </script> <!-- Display a different element depending on the field... --> {#if path === 'thanks_good.1.message_good'} <div class="message"> <h1>Thanks for the feedback ?</h1> <p>We are glad you enjoyed!</p> </div> {:else if path === 'thanks_bad.1.message_bad'} <div class="message"> <h1>We're sorry to hear that ?</h1> <p>We'll do better next time, promise!</p> </div> {:else if path === 'feedback.0.review'} <div class={`review ${status === "error" ? "review--error" : ""}`}> <label for="#review">Could you tell us more?</label> <textarea id="review" on:change={handleReviewChange} /> </div> {:else if path === 'feedback.0.rating'} <!-- Depending on the field status, define some extra classes for styling... --> <div role="button" tabindex="0" class={`rating ${status === "error" ? "rating--error" : ""}`} on:mouseleave={() => { setCurrentRating(currentValue ?? 0); }} > <h1>How would you rate our service?</h1> {#each [1, 2, 3, 4, 5] as rating (rating)} <span role="button" tabindex="0" class={`rating__star ${currentRating >= rating ? "rating__star--active" : ""}`} on:mouseenter={() => { setCurrentRating(rating); }} on:keydown={() => {}} on:click={() => { // On click, notify the form engine about new user input. engine.userAction({ type: "input", path, data: rating }); }} ></span> {/each} </div> {:else} <!-- path === 'feedback.0.submit' --> <button class="submit" on:click={() => { engine.userAction({ type: "input", path, data: true }); }} > Submit </button> {/if}
Di sini, komponen Medan menggunakan prop laluan untuk memutuskan perkara yang hendak dipaparkan:
Mesej "Terima kasih" yang dipaparkan berdasarkan penilaian. Borang akan melaraskan medan dan langkahnya secara dinamik berdasarkan input pengguna.
Cukup keren, bukan?
Sekarang konfigurasi borang dan komponen kami sudah sedia, mari sepadukan mereka ke dalam apl Svelte asas. Berikut ialah kod untuk memulakan dan memberikan borang:
// Let's run the app! // Creating Svelte root... const container = document.querySelector("#root") as unknown as HTMLElement; container.innerHTML = ''; new Form({ props: { Field: Field, configuration: formConfiguration, }, target: container, });
Kod ini melekapkan borang ke DOM. Komponen Borang, yang menghubungkan konfigurasi dan komponen Medan kami, mengendalikan segala-galanya.
Baiklah, kami mempunyai logik apl kami, tetapi jika anda menjalankan kod itu sekarang, anda akan melihat bahawa ia agak... mentah ?
Jadi, mari kita pimp borang dengan menambah beberapa gaya dan animasi! Di bawah ialah helaian gaya ringkas yang menjadikannya lebih menarik:
// 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; } }
Dan voilà ?
Tahniah! ? Anda baru sahaja membina borang maklum balas pengguna dinamik dengan Perseid dan Svelte.
Dalam tutorial ini, kami membincangkan cara untuk:
Jangan ragu untuk bereksperimen dengan medan dan langkah tambahan untuk disesuaikan dengan kes penggunaan anda. Berseronoklah membina borang yang hebat! ?
Atas ialah kandungan terperinci Membina Borang Maklum Balas Pengguna dengan Svelte dan Perseid. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!