@protonemedia/inertia-vue-modal-poc

0.0.1 • Public • Published

Inertia Vue Modal POC

Tested with Vue 2.6.12 + Laravel 8.

I've copied the default Laravel Jetstream modal component for this demo.

Installation

Client-side installation

The only dependency for this POC to work is vue-portal.

In your main JavaScript file, register the Modalable and ToModal components:

import Vue from "vue";
import Modalable from "@/Modal/Modalable";
import ToModal from "@/Modal/ToModal";
import PortalVue from "portal-vue";

Vue.component("Modalable", Modalable);
Vue.component("ToModal", ToModal);
Vue.use(PortalVue)

In your root layout, you need to add the ComponentModal as the last component of your template:

<template>
  <div class="min-h-screen">
    <nav></nav>

    <!-- Page Content -->
    <main>
      <slot />
    </main>

    <ComponentModal />
  </div>
</template>

<script>
import ComponentModal from "@/Modal/ComponentModal";

export default {
  components: {
    ComponentModal,
  },
};
</script>

Server-side installation

In your Laravel application, you only need to add a few lines of code to the HandleInertiaRequests middleware.

  1. Add the handle method to the HandleInertiaRequests middleware.
  2. Add the isModal property to the shared data array.
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Middleware;
use Symfony\Component\HttpFoundation\RedirectResponse;

class HandleInertiaRequests extends Middleware
{
    public function handle(Request $request, Closure $next)
    {
        $response = parent::handle($request, $next);

        if ($response instanceof RedirectResponse && (bool) $request->header('X-Inertia-Modal-Redirect-Back')) {
            return back(303);
        }

        if (Inertia::getShared('isModal')) {
            $response->headers->set('X-Inertia-Modal', true);
        }

        return $response;
    }

    public function share(Request $request)
    {
        return array_merge(parent::share($request), [
            'isModal' => (bool) $request->header('X-Inertia-Modal'),
        ]);
    }
}

Usage

Since we added the ComponentModal component, the global $inertia object now has a visitInModal method. This allows you to make an Inertia visit that loads into the modal.

You can use this method, for example, in the @click handler of a button:

<button @click="$inertia.visitInModal('/user/create')">Load in modal</button>

Instead of using the method in your template, you can also use it in your script:

<script>
export default {
  methods: {
    openModal() {
      this.$inertia.visitInModal('/user/create');
    },
  },
};
</script>

Update the page you want to load into a modal

In most cases, the /user/create endpoint renders a form that's wrapped into a template, maybe with other components and components to style the form. Here's a simple example of what the UserCreate.vue component might look like:

<template>
  <!-- app-layout provides the sidebar navigation and footer -->
  <app-layout>
    <!-- form-panel provides a nice padding with padding and shadow -->
    <form-panel>
      <form @submit.prevent="form.post('/login')">
        <input type="text" v-model="form.name">
        <input type="email" v-model="form.email">

        <button type="submit" :disabled="form.processing">Login</button>
      </form>
    </form-panel>
  </app-layout>
</template>

<script>
export default {
  data() {
    return {
      form: this.$inertia.form({
        name: "",
        email: "",
      }),
    };
  },
};
</script>

To load this form into a modal, we don't want the sidear, footer and styling form the form-panel component. We want just the form itself!

To accomplish this, you need to do three things:

  1. Add the IsModalable mixin to your component.
  2. Wrap your whole component into the Modalable component.
  3. Move the form to a seperate #toModal template and replace it with a ToModal component.
<template>
  <Modalable :is-modal="isModal">
    <app-layout>
      <form-panel>
        <ToModal />
      </form-panel>
    </app-layout>

    <template #toModal>
      <form @submit.prevent="form.post('/login')">
        <!-- name -->
        <input type="text" v-model="form.name">
        <div v-if="form.errors.name">{{ form.errors.name }}</div>

        <!-- email -->
        <input type="email" v-model="form.email">
        <div v-if="form.errors.email">{{ form.errors.email }}</div>

        <!-- submit -->
        <button type="submit" :disabled="form.processing">Login</button>
      </form>
    </template>
  </Modalable>
</template>

<script>
import IsModalable from "@/Modal/IsModalable";

export default {
  mixins: [IsModalable],

  data() {
    return {
      form: this.$inertia.form({
        name: "",
        email: "",
      }),
    };
  },
};
</script>

Now when you visit /user/create, nothing has changed! You still have your layout and form-panel styling. But when you load this component into a modal, it will only render the form.

Handling redirects

By default, redirects are handled as any other Inertia request. For example: you're visiting /user, you open /user/create in a modal, and after a successful submit, you redirect the user to the detail page of the newly created user:

public function store(UserStoreRequest $request)
{
    $user = User::create(...);

    return redirect()->route('user.show', $user);
}

You might not always want to route to the detail page. Luckily, you don't have to update your server-side implementation.

The visitInModal method accepts a second argument, that can either a Boolean or a callback. Instead of redirecting the user, the user stays on the same page and you can manually handle the event with the callback. This callback is executed after a succesful request, for example when the new user is stored in the database.

this.$inertia.visitInModal('/user/create', (event) => {
  // do something
});

Readme

Keywords

none

Package Sidebar

Install

npm i @protonemedia/inertia-vue-modal-poc

Weekly Downloads

2

Version

0.0.1

License

MIT

Unpacked Size

13.5 kB

Total Files

9

Last publish

Collaborators

  • protonemedia