Show Navigation

inertia

Grails Adapter for Inertia.js

Owner: matrei | 1.2.0 | Feb 22, 2023 | Package | Issues | Source | Documentation | License: Apache-2.0


dependencies {
    compile 'io.github.matrei:grails-inertia-plugin:1.2.0'
}

            

Grails Adapter for Inertia.js

Grails plugin for using Inertia.js to build single-page apps without building an API.

What is Inertia.js?

Inertia.js lets you, in its own words, “quickly build modern single-page React, Vue and Svelte apps using classic server-side routing and controllers”.

Using Inertia.js allows using your favorite MVC server-side framework (Grails obviously) with your favorite client-side SPA framework - no need to build a separate API.

Demo

A port (using this plugin) of the original Laravel/PHP Inertia.js Ping CRM demo is available.

Installation

If you don't have an application already:

grails create-app myapp
cd myapp

Add the plugin dependency to the project:

// myapp/build.gradle
dependencies {
    //...
    // Replace $inertiaPluginVersion with a suitable release version for your project, or define it in ~/myapp/gradle.properties
    implementation "io.github.matrei:grails-inertia-plugin:$inertiaPluginVersion"
    //...
}

To add the client dependencies and workflow to a Grails project, create the following files (Vue 3 example):

// myapp/package.json (versions @ 2023-03-01) 
{
  "name": "myapp",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vite --port 3000",
    "build": "vite build"
  },
  "dependencies": {
    "@inertiajs/vue3": "^1.0.2",
    "vue": "^3.2.47"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.0.0",
    "vite": "^4.1.4"
  }
}
// myapp/vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig(({ command }) => ({
  base: command === 'serve' ? '' : '/static/dist/',
  publicDir: false,
  build: {
    manifest: true,
    outDir: 'src/main/resources/public/dist',
    assetsDir: 'js',
    rollupOptions: {
      input: 'src/main/javascript/main.js'
    }
  },
  plugins: [vue()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src/main/javascript', import.meta.url))
    }
  },
  server: {
    // Needed for changes to picked up when running in WSL on Windows
    watch: {
      usePolling: true
    }
  }
}))
// myapp/src/main/javascript/main.js
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'

createInertiaApp({
  resolve: async (name) => {
    const pages = import.meta.glob('./Pages/**/*.vue')
    return (await pages[`./Pages/${name}.vue`]()).default
  },
  setup ({el, App, props, plugin}) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el)
  }
})

It can be a good idea to add the following entries to your .gitignore

# myapp/.gitignore
# ...
node_modules
src/main/resources/public/dist

And run:

npm install

Usage

In your controllers, you can now select which JavaScript Page Component to render and pass the values of the props to it.

// myapp/grails-app/controllers/myapp/BookController.groovy
package myapp

class BookController {
    
    BookService bookService
    
    def index() {
        def books = bookService.listBooks()
        renderInertia 'Books/Index', [books: books]
    }
}

Here is an example Vue 3 Single File Component to that will render the books as a list.

<!-- myapp/src/main/javascript/Pages/Books/Index.vue -->
<script setup>
defineProps({ books: Array })
</script>
<template>
  <ul>
    <li v-for="book in books">{{ book.name }}</li>
  </ul>
</template>

For development with Hot Module Replacement of the application run: (in separate terminals)

npm run serve
./gradlew bootRun

For production or test, first build production version of JavaScript app:

npm run build

and then run whatever you want to do:

./gradlew integrationTest
./gradlew bootJar