55
loading...
This website collects cookies to deliver better user experience
At this moment, Inertia just have three official client-side adapters (React, Vue.js, and Svelte) and two server-side adapters (Laravel and Rails).
<inertia-link>
, when you click on this element, Inertia intercepts this event and makes the visit via XHR
(XMLHttpRequest) instead.div
as a mounting point for the JS side, and this root contains the data-page
attribute with a JSON page object.Inertia uses this page object to share data between client and server, this object contains these properties:
X-Inertia
header set to true
(using the Inertia element anchor), this means that this request is through Inertia so, this the way that server knows if return just HTML or JS Page Components.redirect_to root_path, turbolinks: false
, for now, this the command to create the project:rails new inertia_example --skip-turbolinks
rails new inertia_example --webpacker=vue --skip-turbolinks
rails webpacker:install:vue
yarn add @inertiajs/inertia @inertiajs/inertia-vue @inertiajs/progress
bundle add 'inertia_rails'
inertia_rails
gem, and finally it's necessary add defer: true
to the javascript_pack_tag
into the app/views/layouts/application.html.erb
page:<%= javascript_pack_tag 'application', defer: true %>
defer
we are telling to the application.js
that will be executed after the page has been parsed, avoiding render errors with the dataset
.app/javascript/packs/hello_vue.js
and app/javascript/app.vue
because we don't need them for now and let's initialize Vue inside app/javascript/packs/application.js
// app/javascript/packs/application.js
...
import Vue from 'vue'
import { App, plugin } from '@inertiajs/inertia-vue'
import { InertiaProgress } from '@inertiajs/progress'
Vue.use(plugin)
InertiaProgress.init()
// Instead of using App.vue page, Inertia
// will use Rails application.html.erb layout page
const el = document.getElementById('app')
new Vue({
render: h => h(App, {
props: {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: name => require(`../pages/${name}`).default,
},
}),
}).$mount(el)
app
to the root element, the Inertia adapter will do it for you 👍, initialPage
gets the page where the response from the controller will be inserted, this page is a data attribute inside the root element and resolveComponent
will look at the pages
directory for the views, this is a common convention if you ever used Vue or Nuxt.bundle add tailwindcss-rails
rails tailwindcss:install
stylesheet_link_tag
from your app/views/layouts/application.html.erb
.HomeController
here app/controllers/home_controller.rb
with this 👇🏼class HomeController < ApplicationController
def index
render inertia: 'Home', props: {}
end
end
Home
placed into app/javascript/pages
without any props, just the view, remember the folder pages
was defined in our application.js
in Setting up
section.config/routes.rb
file:Rails.application.routes.draw do
root 'home#index'
end
pages
within app/javascript
dir and we're going to add the Home.vue
file<!-- app/javascript/pages/Home.vue -->
<template>
<div>
<h1 class="text-2xl text-center">Home</h1>
<h2 class="text-xl text-center">This is a Vue page rendered with Inertiajs</h2>
</div>
</template>
body
element in application.html.erb
:<!-- app/views/layouts/application.html.erb -->
...
<body class="p-5 bg-gray-100 mx-auto">
<%= yield %>
</body>
rails s
and to see the changes without reloading the page manually, just run ./bin/webpack-dev-server
in other console tab to watch the frontend changes, if you go to the browser, you'll see something like this:Book
👇🏼rails g model Book title author edition editorial
...
rails db:migrate
# app/controllers/books_controller.rb
class BooksController < ApplicationController
def index
books = Book.all
render inertia: 'Books/Index', props: {
books: books.as_json(only: [:id, :title, :author, :edition, :editorial])
}
end
end
Books/Index
means that we have a folder Books
within app/javascript/pages
and an Index.vue
inside of and we are passing it a books
array with the attributes we defined previously, so let's create the Books/Index.vue
file:<!-- app/javascript/pages/Books/Index.vue -->
<template>
<div>
<h1 class="text-2xl text-center">Books</h1>
<table class="min-w-max w-full table-auto">
<thead>
<tr class="bg-gray-200 text-gray-600 uppercase text-sm leading-normal">
<th class="py-3 px-6 text-left">Title</th>
<th class="py-3 px-6 text-left">Author</th>
<th class="py-3 px-6 text-center">Edition</th>
<th class="py-3 px-6 text-center">Editorial</th>
<th class="py-3 px-6 text-center">Actions</th>
</tr>
</thead>
<tbody class="text-gray-600 text-sm font-light">
<tr v-for="book in books" :key="book.id" class="border-b border-gray-200 hover:bg-gray-100">
<td class="py-3 px-6 text-left"><span class="font-medium">{% raw %}{{ book.title }}{% endraw %}</span></td>
<td class="py-3 px-6 text-left"><span class="font-medium">{% raw %}{{ book.author }}{% endraw %}</span></td>
<td class="py-3 px-6 text-left"><span class="font-medium">{% raw %}{{ book.edition }}{% endraw %}</span></td>
<td class="py-3 px-6 text-left"><span class="font-medium">{% raw %}{{ book.editorial }}{% endraw %}</span></td>
<td class="py-3 px-6 text-left">
<inertia-link :href="$routes.book(book.id)">See</inertia-link>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
props: {
books: {
type: Array,
required: true,
}
}
}
</script>
# config/routes.rb
Rails.application.routes.draw do
root 'home#index'
resources :books, only: %i[index show]
end
db/seeds.rb
and run rails db:seed
to save those data into database10.times do |i|
Book.create(
title: "Metaprogramming Ruby Version-#{i}",
author: "Paolo Perrotta",
edition: "Edition #{i}",
editorial: "The Pragmatic Programmers",
)
end
http://localhost:3000/books
and you should see something like this:<inertia-link>
element, go to books_controller.rb
and create the show method to get a book:def show
book = Book.find(params[:id])
render inertia: 'Books/Show', props: {
book: book.as_json(only: [:id, :title, :author, :edition, :editorial])
}
end
bundle add "js-routes"
config/initializers/jsroutes.rb
with this 👇🏼# config/initializers/jsroutes.rb
JsRoutes.setup do |config|
config.exclude = [/rails_/] # excludes rails generated routes
config.compact = true # removes the _path from the route name
path = "app/javascript/packs" # destination folder
JsRoutes.generate!("#{path}/routes.js")
end
routes.js
and you can use your Rails routes in Javascript, to use them in all places add the following within application.js
:// app/javascript/packs/application.js
...
import Routes from "./routes.js"
Vue.prototype.$routes = Routes
Books/Index.vue
file to add the <inertia-link>
for each book:...
<td class="py-3 px-6 text-left">
<inertia-link :href="$routes.book(book.id)">See</inertia-link>
</td>
book_path(book.id)
in Rails, we are passing the Booki ID to the book URL and finally, we can add the Books/Show.vue
file:<template>
<div class="text-center">
<h1 class="text-4xl font-bold mb-5">{{ book.title }}</h1>
<h2 class="text-xl mb-3">{{ book.author }}</h2>
<p class="font-light italic mb-5">{{ book.edition }} - {{ book.editorial }}</p>
<inertia-link :href="$routes.books()" class="text-blue-500">Back</inertia-link>
</div>
</template>
<script>
export default {
props: {
book: {
type: Object,
required: true
}
},
}
</script>
/books
path and click in some Book on the See
link, and should see the Book view, something like this 👇🏼Network
tab, choose All
and then the request with the Book ID (in my case ID 2), yo can see on Request Headers
section the X-Inertia: true
and the X-Requested-With: XMLHttpRequest
:<inertia-link>
element, because if you reload the page, you're doing a full reload page.55