Multistep form with turbo frame

is there a way to create multistep form with turbo frame?

answer from tonysm

each step, validate each step submission, store the “in progress” data in session when valid (or display the same step form with the invalid messages), then you’d combine the data once the last step is submitted valid.

The only difference would be that the views would use the same Turbo Frame ID, so when they are submitted, only the frame would be replaced with another frame. The trick is to redirect (even if it’s a redirect to the same controller&action with different query strings, such as ?step=2). And return invalid responses with a 422.

Here’s an example:

class RegistrationController extends Controller

{
const STEP_PROFILE = ‘profile’;
const STEP_ADDRESS = ‘address’;
const STEP_CONTACT_INFO = ‘contact’;
const STEP_FINISHED = ‘finished’;

const STEPS_RULES = [
self::STEP_PROFILE => [
‘name’ => [‘required’, ‘string’],
],
self::STEP_ADDRESS => [
‘street_address’ => [‘required’, ‘string’],
],
self::STEP_CONTACT_INFO => [
‘email’ => [‘required’, ‘email’, ‘unique:users,email’,
],
];

public function create(Request $request)
{
return view(‘registration.create’, [
‘step’ => $this->currentStep($request),
]);
}

public function store(Request $request)
{
$currentStep = $this->currentStep($request);
$rules = self::STEPS_RULES[$currentStep];

// Validate each step, if invalid, it will render the form again (default Turbo Laravel behavior).
$data = $request->validate($rules);

$nextStep = match ($currentStep) {
  self::STEP_PROFILE => self::STEP_ADDRESS,
  self::STEP_ADDRESS => self::STEP_CONTACT_INFO,
  default => self::STEP_FINISHED,
};

if ($nextStep === self::STEP_FINISHED) {
  // This is the last stepCreate the profile using current request data and the things in session...
  // Make sure you delete the step from session:

  $request->session()->remove('step');

  return redirect()->route('registration.success');
}

// Store the data in session to be used in the last step and update the step to point to the next one...
$request->session()->put('step', $nextStep);

// On success, redirect back to the create.
return redirect()->route('registrations.create');

}

private function currentStep(Request $request): string
{
return $request->session()->get(‘step’, self::STEP_PROFILE);
}
}
(sorry, code is a little messy, but it’s just a proof of concept)

The view would look something like this:

resources/views/registration/create.blade.php

@include('registration._' . $step)

Since you’re passing the step string to the view, you’d need one partial for each step, so things like:

Step Partial

  1. profile resources/views/registration/_profile.blade.php
  2. address resources/views/registration/_address.blade.php
  3. contact resources/views/registration/_contact.blade.php

All these partials would have a form. The last one (_contact.blade.php) would have a data-turbo-frame="_top" in the form itself, which means when it successfully submits, the entire page will redirect to the success location. When submitted with invalid data, it should just replace the frame with the same form again, but with the invalid messages.

drop your solution