Lopsum's avatar

Livewire refresh the page when submit a form (Vite Dev + Livewire v3)

Hello everyone! šŸ‘‹ I have a small question because I'm trying to make a small installer for my application (Laravel v10, latest version). For this, I'm using Livewire V3.

To do this, I have created 2 middlewares:

1/ First middleware : The first applies to "Web" routes and simply checks whether or not the installation is complete. If not, the user is redirected to the installer.

CheckInstallation.php

public function handle(Request $request, Closure $next): Response
    {
        if(!config('installer.installed'))
        {
            return redirect()->route('setup');
        }

        return $next($request);
    }

Kernel.php

protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            \App\Http\Middleware\CheckInstallation::class,
        ],
...

2/ Second middleware : The second applies to "Setup" routes and redirects the user to the home page if the application is already installed.

redirectIfInstalled.php

public function handle(Request $request, Closure $next): Response
    {
        if (config('installer.installed')) {
            return redirect()->to('/');
        }
        return $next($request);
    }

So I created a new route model "setup.php" which contains all the routes related to the installer in my RouteServiceProvider.

public function boot(): void
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });

        $this->routes(function () {
            Route::middleware('api')
                ->prefix('api')
                ->group(base_path('routes/api.php'));

            Route::middleware('setup')
                ->group(base_path('routes/setup.php'));

            Route::middleware('web')
                ->group(base_path('routes/web.php'));
        });
    }

kernel.php

'setup' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            \App\Http\Middleware\RedirectIfInstalled::class,
        ],

setup.php

Route::get('/setup', \App\Http\Controllers\setup\SetupController::class)->name('setup');

For Livewire to work, I had to add the middleware to the Livewire update route, otherwise, I had a 404 error :

public function boot(): void
    {
        Livewire::setUpdateRoute(function ($handle) {
            return Route::post('/livewire/update', $handle)->middleware('setup');
        });
    }

Well, everything works. I admit I don't know if I did it right.

I do, however, have one small concern. If I run Laravel Vite with npm run dev to have auto refresh, I can't submit a form with Livewire without the page refreshing. If I don't run Vite, but use the build, it works as expected.

Here my little form :

<form id="envForm" wire:submit.prevent="saveEnv">
            @csrf
            <label for="envContent"><h3 class="text-slate-600 dark:text-slate-400 font-bold mb-2">Env file content :</h3></label>
            <textarea id="envContent" name="envContent" rows="4" class="block p-4 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 min-h-[450px]" wire:model="envContent">{!! $envContent !!}</textarea>
        </form>

And the related function in my component :

public function saveEnv(): void
    {
        $this->validate();

        try {
            file_put_contents($this->envPath, $this->only(['envContent']));
        } catch ( Exception $e) {
            $this->hasError = true;
        }
        $this->finalizeInstallation();

    }

If I remove the file_put_content and just put a Log::infos('bla bla bla') in it, the page doesn't refresh.

Do you have any idea what might be causing the page to refresh? Thanks in advance šŸ˜‡

0 likes
1 reply
LaryAI's avatar
Level 58

It seems like the issue you're experiencing with the page refresh when submitting a Livewire form while running Laravel Vite with npm run dev might be related to how Vite handles HMR (Hot Module Replacement) and how Livewire is trying to prevent the default form submission behavior.

Here are a few steps you can take to troubleshoot and potentially solve the issue:

  1. Ensure Livewire Scripts are Loaded Correctly: Make sure that you have included @livewireScripts in your blade layout or specific blade file where you're using Livewire components.

  2. Check for JavaScript Errors: Open your browser's developer console and check for any JavaScript errors that might be preventing Livewire's scripts from intercepting the form submission.

  3. Verify Livewire Component Setup: Ensure that your Livewire component is set up correctly and that the saveEnv method is being called without any issues.

  4. Examine Network Requests: Use the network tab in your browser's developer tools to inspect the XHR requests when you submit the form. Livewire should make an AJAX request without a full page reload.

  5. Disable HMR Temporarily: Try running Vite without HMR to see if the issue persists. You can do this by modifying your Vite configuration to disable HMR or by using the --force flag when running npm run dev.

  6. Check for Conflicts with Middleware: Since you've added the setup middleware to the Livewire update route, ensure that there are no conflicts or unintended redirects happening as a result of this middleware.

  7. Livewire Event Listeners: Livewire offers a way to listen for browser events and take actions accordingly. Ensure that there are no event listeners that might be causing the page to refresh.

  8. Update Livewire and Vite: Ensure that you are using the latest versions of Livewire and Vite, as updates may contain bug fixes that could resolve your issue.

If none of these steps resolve the issue, you might want to consider creating a minimal reproduction of the problem and submitting it to the Livewire GitHub repository or the Vite GitHub repository for further assistance from the community.

Here's a minimal example of how your Livewire component and form should generally look:

// In your Livewire component
public $envContent;

public function saveEnv()
{
    $this->validate([
        'envContent' => 'required', // Add your validation rules here
    ]);

    try {
        file_put_contents($this->envPath, $this->envContent);
        session()->flash('message', 'Environment settings saved successfully.');
    } catch (Exception $e) {
        $this->hasError = true;
        session()->flash('error', 'Failed to save environment settings.');
    }

    $this->finalizeInstallation();
}
{{-- In your Blade view --}}
<form wire:submit.prevent="saveEnv">
    @csrf
    <label for="envContent">
        <h3 class="text-slate-600 dark:text-slate-400 font-bold mb-2">Env file content:</h3>
    </label>
    <textarea id="envContent" name="envContent" rows="4" class="block p-4 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 min-h-[450px]" wire:model="envContent"></textarea>
    <button type="submit">Save</button>
</form>

@livewireScripts

Remember to include @livewireStyles in the head of your Blade layout and @livewireScripts before the closing body tag to ensure Livewire's styles and scripts are loaded correctly.

Please or to participate in this conversation.