Znajdziesz nas na:
27.06.26 3 min czytania Technologia

Jak tworzyć dynamiczne formularze w Storyblok i Nuxt z Vuelidate?

Storyblok and Vue.js logos

Dynamiczne formularze w Storyblok pozwalają redaktorom dodawać i konfigurować pola formularza z poziomu CMS, a Nuxt odpowiada za ich renderowanie po stronie frontendu. Do walidacji możemy wykorzystać Vuelidate, dzięki czemu reguły takie jak required, email czy minLength również mogą wynikać z konfiguracji w Storyblok. W tym tutorialu pokażemy, jak połączyć Storyblok, Nuxt i Vuelidate, aby zbudować formularz, którego pola i reguły walidacji są definiowane dynamicznie.

ElementRola
Storyblokdefiniuje pola formularza i reguły
Nuxt / Vuerenderuje formularz
Vuelidatewaliduje wartości pól
DynamicForm.vuebuduje formularz i reguły
InputField.vuerenderuje pojedyncze pole

 

 

 

 

 

 

 

 

 

Jak skonfigurować Vuelidate w Nuxt?

Do obsługi walidacji dynamicznych pól wykorzystamy Vuelidate. Najpierw dodaj do projektu dwa wymagane pakiety:

npm install @vuelidate/core @vuelidate/validators

lub:

yarn add @vuelidate/core @vuelidate/validators

W aktualnej wersji Vuelidate nie rejestrujemy biblioteki jako globalnej wtyczki Nuxt. Funkcję useVuelidate() importujemy i uruchamiamy bezpośrednio w komponencie, w którym definiujemy stan formularza i reguły walidacji. Oznacza to, że plik plugins/vuelidate.ts nie jest potrzebny.

W komponencie formularza będziemy korzystać z:

import { useVuelidate } from '@vuelidate/core'

import * as validators from '@vuelidate/validators'

Konfiguracja dynamicznego formularza w Storyblok

Po zainstalowaniu Vuelidate możemy przygotować w Storyblok strukturę formularza. Storyblok będzie przechowywał konfigurację pól i reguł walidacji, natomiast Nuxt i Vuelidate wykorzystają te dane do wyrenderowania formularza i sprawdzenia wartości wpisywanych przez użytkownika.

Bloki walidacji
Aby walidację można było konfigurować bezpośrednio w Storyblok, tworzymy osobne bloki odpowiadające regułom Vuelidate. W tym przykładzie wykorzystamy:

  • required - pole musi mieć wartość,
  • email - wartość musi mieć poprawny format adresu e-mail,
  • numeric - pole może zawierać wyłącznie wartość numeryczną,
  • minLength - określa minimalną długość wartości,
  • maxLength - określa maksymalną długość wartości.
Storyblok Block Library showing reusable form validation blocks for email, maximum and minimum length, numeric fields and required fields

W tym przykładzie nazwy bloków odpowiadają nazwom walidatorów z Vuelidate, ponieważ później wykorzystamy je do dynamicznego przypisywania reguł w komponencie Vue.

Bloki email, numeric i required mają pole errorMessage, w którym redaktor może określić komunikat wyświetlany użytkownikowi po błędnej walidacji.

Storyblok editor for an email validation block with an error message field configuration

Dla minLength i maxLength dodajemy również pole param, które przechowuje wymaganą liczbę znaków.

Storyblok editor for a maxLength validation block with error message and numeric parameter fields

Uwaga: Pole Required dostępne w konfiguracji pól Storyblok dotyczy kompletności treści w samym CMS. Reguły opisane powyżej służą natomiast do walidacji formularza po stronie aplikacji.

Blok pola formularza
Następnie tworzymy blok inputField, który opisuje pojedyncze pole dynamicznego formularza.

Storyblok editor for an input field block with name, type, label, placeholder and validation settings

Komponent składa się z pola:

  • name: pole tekstowe, dzięki któremu nadamy polu formularza unikalny identyfikator <input name="" />,
  • type: pole pojedyńczego wyboru, z nadanymi opcjami: text, tel, email. Będzie określać typ pola formularza <input type="" />,
  • label: pole tekstowe, używane jako nazwa etykiety pola fomularza,
  • placeholder: pole tesktowe, które wyświetli się w polu formularza gdy będzie puste <input placeholder="" />,
  • validators: bloki z listą walidacji wcześniej przez nas utworzonych.

Komponent formularza
Teraz tworzymy dynamiczny komponent formularza dynamicForm.

Storyblok editor for a dynamic form block with input fields, form endpoint and submit button text settings

Składa się on z:

  • inputs: zawiera listę pól formularza (inputField),
  • formEndpoint: adres URL, pod który formularz zostanie wysłany,
  • submitButtonText: nazwa jaka wyświetli się w przycisku zatwierdzającym wysłanie formularza.

Komponenty Vue
Kiedy mamy już przygotowane komponenty i pola po stronie Storybloka, możemy przejść do tworzenia komponentów we Vue. Potrzebujemy dwóch komponentów: DynamicForm.vue, który zawierać będzie dynamiczny formularz, oraz InputField.vue, gdzie będzie się mieścić pojedyncze pole formularza.

DynamicForm.vue
Logika
Na początku należy zaimportować walidatory z pakietu Vuelidate oraz funkcję useVualidate do aktywowania walidacji.

import { useVuelidate } from '@vuelidate/core'

import * as validators from '@vuelidate/validators'

W komponencie należy również zadeklarować props pochodzące ze Storybloka.

const DynamicFormProps = defineProps({

 blok: {

   type: Object,

   default: () => ({})

 }

})

Początkowe dane formularza są generowane za pomocą funkcji form. Tworzy ona obiekt, używając do tego unikalnej nazwy pola formularza jako klucz oraz pustego łańcucha znaków jako wartości.

const form = reactive(DynamicFormProps.blok.inputs.reduce(

 (prevFields, inputField) => ({

   ...prevFields,

   [inputField.name]: ''

 }),

 {}

))

Funkcja generateFieldRules() odpowiada za generowanie walidatorów dla danego pola formularza. Tworzymy tu obiekt składający się z nazwy walidatora jako klucz, a wartością jego jest dostarczony przez pakiet Vuelidate domyślny komunikat o błędach lub funkcja, jeżeli element posiada parametr (np. dla walidacji, która definiuje minimalną długość tekstu, należy wywołać funkcje minLenght() i przekazać w niej parametr określający liczbę znaków).

const generateFieldRules = (fieldValidators) => {

 return fieldValidators.reduce(

   (prevValidators, validator) => ({

     ...prevValidators,

     [validator.component]: validator.param ? validators[validator.component](validator.param) : validators[validator.component]

   }),

   {}

 )

}

W zmiennej fieldRules trzymamy nazwy pól formularza z wygenerowanymi walidatorami.

const fieldRules = computed(() => {

 return DynamicFormProps.blok.inputs.reduce(

   (prevFields, inputField) => ({

     ...prevFields,

     [inputField.name]: generateFieldRules(inputField.validators)

   }),

   {}

 )

})

Aby walidacja zaczęła działać, należy aktywować pakiet Vuelidate poprzez wykonanie metody useVualidate, która przyjmuje jako parametry stworzone przez nas wcześniej zmienne. Dzięki temu za pośrednictwem v$ uzyskuje się dostęp do danych oraz opcji.

const v$ = useVuelidate(fieldRules, form)

Ostatnią funkcją jest obsłużenie wysyłki formularza - formSubmit(). Sprawdzając wartość zmiennej $invalid dostajemy informację czy jakiekolwiek pole w formularzu nie przeszło walidacji. W takim wypadku wywołujemy metodę $touch(), która powoduje wyświetlenie się ostrzeżeń o błędach w poszczególnych polach formularza.

const formSubmit = (e) => {

 if (v$.value.$invalid) {

   v$.value.$touch()

   e.preventDefault()

 }

}

Aby komponent InputFields.vue mógł korzystać z walidacji formularza należy mu dostarczyć zmienną v$

provide('v$', v$)

Łącząc wszystkie opisane elementy, dostajemy następującą logikę:

<script setup>

import { useVuelidate } from '@vuelidate/core'

import * as validators from '@vuelidate/validators'

const DynamicFormProps = defineProps({

blok: {

  type: Object,

  default: () => ({})

}

})

 

const form = reactive(DynamicFormProps.blok.inputs.reduce(

(prevFields, inputField) => ({

  ...prevFields,

  [inputField.name]: ''

}),

{}

))

const generateFieldRules = (fieldValidators) => {

return fieldValidators.reduce(

  (prevValidators, validator) => ({

    ...prevValidators,

    [validator.component]: validator.param ? validators[validator.component](validator.param) : validators[validator.component]

  }),

  {}

)

}

 

const fieldRules = computed(() => {

return DynamicFormProps.blok.inputs.reduce(

  (prevFields, inputField) => ({

    ...prevFields,

    [inputField.name]: generateFieldRules(inputField.validators)

  }),

  {}

)

})

 

const formSubmit = (e) => {

if (v$.value.$invalid) {

  v$.value.$touch()

  e.preventDefault()

}

}

 

const v$ = useVuelidate(fieldRules, form)

provide('v$', v$)

</script>

Szablon komponentu formularza
W szablonie tym tworzymy formularz, który posiada:

  • action - atrybut określający gdzie wysłać dane formularza po jego przesłaniu,
  • formSubmit - wywołanie metody przy próbie wysyłki formularza,
  • InputField - komponent zawierający pojedyncze pole formularza,
  • button - przycisk wywołujący wysyłkę formularza.

<template>

 <form v-if="v$" :id="blok._uid" class="form" method="post" :action="blok.formEndpoint" @submit="formSubmit">

   <InputField v-for="inputField in blok.inputs" :key="inputField.name" :inputField="inputField" />

   <button type="submit" class="btn">

     {{ blok.submitButtonText }}

   </button>

 </form>

</template>

InputField.vue
Logika
Na początku należy zaimportować funkcje pozwalającą przyjmowanie danych.

import { inject } from 'vue'

Następnie zadeklarować props pochodzących z komponentu DynamicForm.vue

defineProps({ inputField: Object })

A na koniec stworzyć zmienną, dzięki której będziemy mieć dostęp do walidacji formularza

const v$ = inject('v$')

Szablon komponentu pola formularza
Pojedyncze pole formularza składa się z:

  • Sprawdzenia, czy pole zostało poprawnie uzupełnione poprzez zmienną v$[inputField.name].$error. W przypadku nieprawidłowej zawartości zostaje dynamicznie dodana dodatkowa klasa.
  • Powiązania danych pola za pomocą dyrektywy v-model między elementem a modelem danych Vualidate ($model).
  • Obsłużonych błędów walidacji. Dzieje się to za pomocą iteracji po błędach. Jeśli pole posiada dany problem, jest on wyświetlany.

<template>

<div

  :class="{

    'form__group': true,

    'form__group--error': v$[inputField.name].$error,

  }"

>

  <label class="form__label" :for="inputField._uid">{{ inputField.label }}</label>

  <input

    :id="inputField._uid"

    v-model.trim="v$[inputField.name].$model"

    :type="inputField.type"

    :name="inputField.name"

    :placeholder="inputField.placeholder"

    class="form__input"

  >

  <div v-if="v$[inputField.name].$error">

    <div v-for="{ component, errorMessage } in inputField.validators" :key="component" class="form__group__warninig">

      <div v-if="v$[inputField.name][component].$invalid">

        {{ errorMessage }}

      </div>

    </div>

  </div>

</div>

</template>

Podsumowanie

Każdy developer prędzej czy później stoi przed wyzwaniem, jakim jest stworzenie dynamicznych formularzy. Dzięki nim klient sam może dodawać oraz edytować pola w formularzach bez ingerencji programisty. 
Przedstawiony przykład można rozwinąć poprzez stworzenie dodatkowych typów pól jak radio, checkbox czy textarea.

Tandemite team: Monika_avatar
Monika Harewska
Frontend developer @ Tandemite

FAQ

Jak połączyć Storyblok z Vuelidate?

W Storyblok definiujesz strukturę formularza, pola oraz reguły walidacji, a Vuelidate odpowiada za sprawdzanie wartości po stronie aplikacji. Nazwy bloków walidacyjnych, np. required, email czy minLength, mogą odpowiadać nazwom walidatorów Vuelidate. Komponent Vue odczytuje konfigurację ze Storyblok, dynamicznie buduje reguły i przekazuje je do useVuelidate().

Czy redaktor może dodawać pola formularza bez zmian w kodzie?

Tak, jeśli dany typ pola został wcześniej obsłużony w komponencie formularza. Redaktor może wtedy dodawać kolejne bloki inputField, ustalać ich nazwę, etykietę, placeholder, typ oraz przypisywać reguły walidacji bez edytowania kodu Vue. Dodanie całkowicie nowego typu pola, którego frontend jeszcze nie obsługuje, wymaga wcześniejszego rozszerzenia komponentu przez developera.

Jak dodać nowy typ walidacji do dynamicznego formularza?

Jeżeli walidator jest dostępny w Vuelidate, możesz utworzyć w Storyblok odpowiadający mu blok i użyć takiej samej nazwy jak w bibliotece. W razie potrzeby blok może również przechowywać parametr, np. minimalną długość tekstu, oraz własny komunikat o błędzie. Logika generująca reguły w Vue odczyta tę konfigurację i przypisze właściwy walidator. Własne, niestandardowe reguły wymagają dodania odpowiedniej logiki w kodzie.

Jak dodać pola checkbox, radio lub textarea?

Obecny przykład wykorzystuje pola oparte na <input>, ale tę samą architekturę można rozszerzyć o checkbox, radio i textarea. Najpierw trzeba dodać odpowiednie typy do konfiguracji w Storyblok, a następnie obsłużyć ich renderowanie w komponencie InputField.vue. Po wykonaniu tej zmiany redaktor może wybierać nowe typy pól z poziomu Storyblok tak samo jak obecne pola tekstowe.

Znajdź coś dla siebie

Więcej artykułów po tagach

Masz pomysł, ale nie wiesz, jak go zrealizować? Pytaj śmiało!

Skorzystaj z bezpłatnej konsultacji
4.9 w ocenie naszych klientów na clutch

Przeczytaj najlepsze branżowe wskazówki od ekspertów PIM. Za darmo!

Napisz do nas

Czekamy na Twoją wiadomość

Tandemite icon: clock

Szybki kontakt

Skontaktujemy się z Tobą w ciągu 24 godzin, żeby jak najszybciej poznać Twoje potrzeby.

Tandemite icon: paper airplane

Precyzyjna reakcja

Przygotujemy estymację Twojego projektu, uwzględniającą koszty i czas wykonania.

* Pola oznaczone gwiazdką są wymagane
lub upuść brief swojej firmy tutaj. PDF lub DOCX
Więcej informacji o Twoich prawach, w Polityce prywatności i cookie
Ta witryna jest chroniona przez reCAPTCHA i Google Obowiązują Polityka prywatności