Membina Borang Maklum Balas Pengguna dengan Svelte dan Perseid

Patricia Arquette
Lepaskan: 2024-10-02 06:35:02
asal
260 orang telah melayarinya

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 1: Menyediakan Konfigurasi Borang

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"],
    },
  },
};

Salin selepas log masuk

Dalam konfigurasi ini:

  • Borang bermula pada langkah maklum balas.
  • Borang mengandungi dua medan: rating (diperlukan) dan ulasan (pilihan melainkan rating di bawah 3).
  • Berdasarkan penilaian, borang tersebut menavigasi ke sama ada mesej maklum balas "baik" atau "buruk".
  • Setelah penyerahan borang, makluman mudah dicetuskan dengan data yang diserahkan.

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.


Langkah 2: Mencipta Komponen Svelte Borang

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}
Salin selepas log masuk

Di sini, komponen Medan menggunakan prop laluan untuk memutuskan perkara yang hendak dipaparkan:

  • Komponen rating yang membolehkan pengguna memilih rating bintang.
  • Kawasan teks untuk pengguna memberikan maklum balas tambahan.

Mesej "Terima kasih" yang dipaparkan berdasarkan penilaian. Borang akan melaraskan medan dan langkahnya secara dinamik berdasarkan input pengguna.

Cukup keren, bukan?

Building a User Feedback Form with Svelte and Perseid


Langkah 3: Menjalankan Aplikasi

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,
});
Salin selepas log masuk

Kod ini melekapkan borang ke DOM. Komponen Borang, yang menghubungkan konfigurasi dan komponen Medan kami, mengendalikan segala-galanya.

Langkah 4: Menambah Gaya

Baiklah, kami mempunyai logik apl kami, tetapi jika anda menjalankan kod itu sekarang, anda akan melihat bahawa ia agak... mentah ?

Building a User Feedback Form with Svelte and Perseid

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;
  }
}
Salin selepas log masuk

Dan voilà ?


Kesimpulan

Tahniah! ? Anda baru sahaja membina borang maklum balas pengguna dinamik dengan Perseid dan Svelte.

Dalam tutorial ini, kami membincangkan cara untuk:

  • Tentukan konfigurasi borang dengan logik bersyarat.
  • Bina komponen Svelte tersuai untuk mengendalikan interaksi pengguna.
  • Render borang dalam apl anda dan gayakannya dengan animasi dan CSS tersuai.

Jangan ragu untuk bereksperimen dengan medan dan langkah tambahan untuk disesuaikan dengan kes penggunaan anda. Berseronoklah membina borang yang hebat! ?


  • ? Lagi contoh
  • ✅ Dokumentasi lengkap
  • ? Sertai Discord kami
  • ? Bintangkan projek di GitHub
  • ❤️ Taja Perseid

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!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan