All Projects → bayareawebpro → Laravel Multistep Forms

bayareawebpro / Laravel Multistep Forms

Licence: mit
Responsable Multistep Form Builder for Laravel

Projects that are alternatives of or similar to Laravel Multistep Forms

Form Object
Form object to use with Vue components for sending data to a Laravel application using axios.
Stars: ✭ 73 (-3.95%)
Mutual labels:  validation, laravel, forms
Laravel Jsonapi
Basic setup framework for creating a Laravel JSON-API server
Stars: ✭ 16 (-78.95%)
Mutual labels:  validation, laravel
React Final Form
🏁 High performance subscription-based form state management for React
Stars: ✭ 6,781 (+8822.37%)
Mutual labels:  validation, forms
Laravel Vue Validator
Simple package to display error in vue from laravel validation
Stars: ✭ 32 (-57.89%)
Mutual labels:  validation, laravel
Typesystem
Data validation, serialization, deserialization & form rendering. 🔢
Stars: ✭ 416 (+447.37%)
Mutual labels:  validation, forms
Winterfell
Generate complex, validated and extendable JSON-based forms in React.
Stars: ✭ 787 (+935.53%)
Mutual labels:  validation, forms
Validating
Automatically validating Eloquent models for Laravel
Stars: ✭ 906 (+1092.11%)
Mutual labels:  validation, laravel
Validator Docs
Validação de CPF, CNPJ, CNH, NIS, Título Eleitoral e Cartão Nacional de Saúde com Laravel.
Stars: ✭ 334 (+339.47%)
Mutual labels:  validation, laravel
Usetheform
React library for composing declarative forms, manage their state, handling their validation and much more.
Stars: ✭ 40 (-47.37%)
Mutual labels:  validation, forms
Formik Alicante
Formik slides & demos from React Alicante
Stars: ✭ 47 (-38.16%)
Mutual labels:  validation, forms
Laravel5 Genderize Api Client
Laravel 5 client for the Genderize.io API
Stars: ✭ 47 (-38.16%)
Mutual labels:  validation, laravel
Colander
A serialization/deserialization/validation library for strings, mappings and lists.
Stars: ✭ 408 (+436.84%)
Mutual labels:  validation, forms
React Hook Form
📋 React Hooks for form state management and validation (Web + React Native)
Stars: ✭ 24,831 (+32572.37%)
Mutual labels:  validation, forms
Laravel Validation Rules
A set of useful Laravel validation rules
Stars: ✭ 374 (+392.11%)
Mutual labels:  validation, laravel
Laravel Casts
Form builder for Laravel.
Stars: ✭ 56 (-26.32%)
Mutual labels:  laravel, forms
Identity Number
Validator for Swedish personal identity numbers (personnummer). For use "standalone" or with Laravel.
Stars: ✭ 17 (-77.63%)
Mutual labels:  validation, laravel
Forms
📝 Generating, validating and processing secure forms in PHP. Handy API, fully customizable, server & client side validation and mature design.
Stars: ✭ 272 (+257.89%)
Mutual labels:  validation, forms
Laravel Postal Code Validation
Worldwide postal code validation for Laravel and Lumen
Stars: ✭ 278 (+265.79%)
Mutual labels:  validation, laravel
Validator
Client-side javascript validator library ports from Laravel 5.2
Stars: ✭ 35 (-53.95%)
Mutual labels:  validation, laravel
Laravel Smart
Automatic Migrations, Validation and More
Stars: ✭ 48 (-36.84%)
Mutual labels:  validation, laravel

Laravel MultiStep Forms

https://packagist.org/packages/bayareawebpro/laravel-multistep-forms

Multistep Form Builder is a "responsable" class that can be returned from controllers.

  • Specify a view to use Blade or go headless with JSON for use with Javascript frameworks.
  • Configure the rules, messages and supporting data for each step with simple arrays.
  • Submit to the same route multiple times to merge each validated request into a namespaced session key.
  • Hook into each step before or after validation to interact with the form or return a response.

Installation

composer require bayareawebpro/laravel-multistep-forms

Example Usage

<?php

use BayAreaWebPro\MultiStepForms\MultiStepForm as Form;

// Render a view with data.
return Form::make('my-form', [
        'title' => 'MultiStep Form'
    ])

    // Namespace the session data.
    ->namespaced('my-session-key')

    // Allow backwards navigation via get request. ?form_step=x
    ->canNavigateBack(true)

    // Before x step validation...
    ->beforeStep('*', function (Form $form) {
       logger('before*', $form->toArray());
    })

    // After x step...
    ->onStep('*', function (Form $form) {
       logger('on*', $form->toArray());
    })

    // Validate Step 1
    ->addStep(1, [
        'rules' => ['name' => 'required'],
        'messages' => ['name.required' => 'Your name is required silly.'],
    ])

    // Validate Step 2
    ->addStep(2, [
        'rules' => ['role' => 'required|string'],
        'data' => ['roles' => Roles::forSelection()]
    ])

    // Add non-validated step...
    ->addStep(3,[
       'data' => ['message' => "Great Job, Your Done!"]
    ])

    // Tap Invokable Class __invoke(Form $form)
    ->tap(new InvokableClass)

    // After step validation...
    ->onStep(3, function (Form $form) {
       logger('onStep3', $form->toArray());
       if($form->request->get('submit') === 'reset'){
            $form->reset();
       }else{
           return response('OK');
       }
    })
;

Make New Instance

Make a new instance of the builder class with optional view and data array. You should always set the namespace for the form session to avoid conflicts with other parts of your application that use the session store.

  • GET requests will load the form state and data for the saved current step or fallback to step 1.
  • POST,PUT,PATCH etc... will validate and process the request for any step and proceed to the next configured step.
  • Backwards navigation can be enabled via the canNavigateBack method.
<?php

use BayAreaWebPro\MultiStepForms\MultiStepForm as Form;

$form = Form::make('onboarding.start', [
	'title' => 'Setup your account'
]);

$form->namespaced('onboarding');
$form->canNavigateBack(true);

Configure Steps

Define the rules, messages and data for the step. Data will be merged with any view data defined in the make method.

Use an array:

$form->addStep(1, [
    'rules' => [
        'name' => 'required|string'
    ],
    'messages' => [
        'name.required' => 'Your name is required silly.'
    ],
    'data' => [
        'placeholders' => [
            'name' => 'Enter your name.'
        ]
    ],
])

Or use a class that returns an array (recommended)

$form->addStep(1, MyStep1::make());

Before Step Hooks

Define a callback to fired before a step has been validated. Step Number or * for all.

Return a response from this hook to return early before validation occurs.

beforeStep($step, Closure $closure)


On Step Hooks

Define a callback to fired after a step has been validated. Step Number or * for all.

Return a response from this hook to return early before the form step is incremented.

onStep($step, Closure $closure)


Helper Methods

stepConfig(?int $step = null)

Get the current step config, or a specific step config.

getValue(string $key, $fallback = null)

Get a field value from the form state (session / old input) or fallback to a default.

setValue(string $key, $value)

Set a field value from the session form state.

currentStep()

Get the current saved step number.

requestedStep()

Get the requested step number.

isStep(int $step = 1)

Get the current step number.

isLastStep()

Determine if the current step the last step.

isPast(int $step, $truthy = true, $falsy = false)

Determine if the specified step is in the past and optionally pass through values (class helper).

isActive(int $step, $truthy = true, $falsy = false)

Determine if the specified step is active and optionally pass through values (class helper).

isFuture(int $step, $truthy = true, $falsy = false)

Determine if the specified step is in the future and optionally pass through values (class helper).

reset($data = [])

Reset the form state to defaults passing an optional array of data to seed.

tap(new Invokable)

Tap into the builder instance with invokeable classes that will be pass an instance of the form.

toCollection

Get the array representation of the form state as a collection.

toArray

Get the array representation of the form state.


Blade Example

Data will be injected into the view as well as the form itself allowing you to access the form values and other helper methods.

<?php
use BayAreaWebPro\MultiStepForms\MultiStepForm as Form;

$form = Form::make('my-view', $data);
$form->namespaced('onboarding');
$form->canNavigateBack(true);
{{ $form->toCollection() }}
{{ $myDataKey }}

<form method="post" action="{{ route('submit') }}">
    <input type="hidden" name="form_step" value="{{ $form->currentStep() }}">
    @csrf


    <a
        href="{{ route('submit', ['form_step' => 1]) }}"
        class="{{ $form->isPast(1, 'text-blue-500', $form->isActive(1, 'font-bold', 'disabled')) }}">
        Step 1
    </a>
    <a
        href="{{ route('submit', ['form_step' => 2]) }}"
        class="{{ $form->isPast(2, 'text-blue-500', $form->isActive(2, 'font-bold', 'disabled')) }}">
        Step 2
    </a>
    <a
        href="{{ route('submit', ['form_step' => 3]) }}"
        class="{{ $form->isPast(3, 'text-blue-500', $form->isActive(3, 'font-bold', 'disabled')) }}">
        Step 3
    </a>

    
    @switch($form->currentStep())
    
        @case(1)
            <label>Name</label>
            <input type="text" name="name" value="{{ $form->getValue('name') }}">
            @error('name') 
                <p>{{ $errors->first('name') }}</p>
            @enderror
        @break
    
        @case(2)
            <label>Role</label>
            <input type="text" name="role" value="{{ $form->getValue('role') }}">
             @error('role') 
                <p>{{ $errors->first('role') }}</p>
            @enderror
        @break
    
        @case(3)
            <p>Review your submission:</p>
            <p>
             Name: {{ $form->getValue('name') }}<br>
             Role: {{ $form->getValue('role') }}<br>
            </p>
        @break
    
    @endswitch
    
    @if($form->isLastStep())
        <button type="submit" name="submit">Save</button>
        <button type="submit" name="submit" value="reset">Reset</button>
    @else
        <button type="submit" name="submit">Continue</button>
    @endif

</form>

Vue Example

Form state and data will be returned as JSON when no view is specified or the request prefers JSON. You can combine both techniques to use Vue within blade as well.

<?php

use BayAreaWebPro\MultiStepForms\MultiStepForm as Form;

$form = Form::make();
$form->namespaced('onboarding');
$form->canNavigateBack(true);

JSON Response Schema

The response returned will have two properties:

{
	"form": {},
	"data": {}
}
<v-form action="{{ route('submit') }}">
    <template v-slot:default="{form, options, errors, reset, back}">

        <h1 class="font-black my-3">
            @{{ options.title }}
        </h1>

        <p v-if="options.message" role="alert" class="bg-gray-200 p-4 my-5 font-bold text-blue-500">
            @{{ options.message }}
        </p>

        <template v-if="form.form_step < 4">
            <a
                @click="back(1)"
                :class="{'text-blue-500': form.form_step > 1, 'font-bold': form.form_step === 1}">
                Step 1
            </a>
            <a
                @click="back(2)"
                :class="{'text-blue-500': form.form_step > 2, 'font-bold': form.form_step === 2}">
                Step 2
            </a>
            <a
                @click="back(3)"
                :class="{'text-blue-500': form.form_step > 3, 'font-bold': form.form_step === 3}">
                Step 3
            </a>
        </template>

        <template v-if="form.form_step === 1">

           <v-input
               name="name"
               label="Name"
               :errors="errors"
               v-model="form.name">
           </v-input>

           <v-select
               name="name"
               label="Name"
               :errors="errors"
               :options="options.roles"
               v-model="form.role">
           </v-select>

            <x-action>Continue</x-action>
        </template>

        <template v-if="form.form_step === 2">
            <v-input
                name="email"
                label="Email"
                :errors="errors"
                v-model="form.email">
            </v-input>
            <v-input
                name="phone"
                label="Phone"
                :errors="errors"
                v-model="form.phone">
            </v-input>
            <x-action>Continue</x-action>
        </template>

        <template v-if="form.form_step === 3">
            <v-input
                name="bio"
                label="Bio"
                :errors="errors"
                v-model="form.bio">
            </v-input>
            <v-input
                name="notify"
                label="Notify"
                :errors="errors"
                v-model="form.notify">
            </v-input>
            <x-action>Continue</x-action>
        </template>

        <template v-if="form.form_step === 4">
            <h3>Review Submission</h3>
            <p>
                Name: @{{ form.name }}<br>
                Role: @{{ form.role }}<br>
                Email: @{{ form.email }}<br>
                Phone: @{{ form.phone }}<br>
            </p>
            <x-action>Save</x-action>
            <x-action @click="reset">Reset</x-action>
        </template>

        <template v-if="form.form_step === 5">
            <x-action>Done</x-action>
        </template>

    </template>
</v-form>

Example Form Component

<script>
    export default {
        name: 'Form',
        props: ['action'],
        data: () => ({
            errors: {},
            options: {},
            form: {form_step: 1},
        }),
        methods: {
            reset() {
                this.form.submit = 'reset'
                this.submit()
            },
            back(step) {
                if(step < this.form.form_step){
                    this.fetch({form_step: step})
                }
            },
            fetch(params = {}) {
                axios
                    .get(this.action, {params})
                    .then(this.onResponse)
                    .catch(this.onError)
            },
            submit() {
                axios
                    .post(this.action, this.form)
                    .then(this.onResponse)
                    .catch(this.onError)
            },
            onError({response}) {
                this.errors = (response.data.errors || response.data.exception)
            },
            onResponse({data}) {
                this.errors = {}
                this.options = (data.data || {})
                this.form = (data.form || {})
            },
        },
        created() {
            this.fetch()
        }
    }
</script>
<template>
    <form @submit.prevent="submit">
        <slot :reset="reset" :back="back" :form="form" :options="options" :errors="errors"/>
    </form>
</template>

Example Input Component

<script>
    export default {
        name: "Input",
        props:['name', 'label', 'value', 'errors'],
        computed:{
            field:{
                get(){
                    return this.value
                },
                set(val){
                    return this.$emit('input', val)
                }
            }
        }
    }
</script>
<template>
    <label class="block my-4">
        <span class="text-gray-700 font-bold">
            {{ label || name }}
        </span>
        <input
            type="text"
            v-model="field"
            class="form-input block w-full mt-2">
        <div v-if="errors[name]" class="text-red-500 text-xs my-2">
            {{ errors[name][0] }}
        </div>
    </label>
</template>

Example Select Component

<script>
    export default {
        name: "Select",
        props:['name', 'label', 'value', 'errors', 'options'],
        computed:{
            field:{
                get(){
                    return this.value
                },
                set(val){
                    return this.$emit('input', val)
                }
            }
        }
    }
</script>
<template>
    <label class="block">
        <span class="text-gray-700">{{ label || name }}</span>
        <select v-model="field" class="form-select mt-1 block w-full">
            <option disabled value="">Please select one</option>
            <option v-for="option in options" :value="option">
                {{ option }}
            </option>
        </select>
        <div v-if="errors[name]" class="text-red-500 text-xs my-2">
            {{ errors[name][0] }}
        </div>
    </label>
</template>
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].