Skip to content

Instantly share code, notes, and snippets.

@reinink
Last active October 22, 2023 13:06
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save reinink/65f85856d4f4674be95f1579989e6b84 to your computer and use it in GitHub Desktop.
Save reinink/65f85856d4f4674be95f1579989e6b84 to your computer and use it in GitHub Desktop.
Multi-page Vue App in Laravel
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{ isset($title) ? $title.' - '.Config::get('app.name') : Config::get('app.name') }}</title>
<link href="{{ mix('/css/app.css') }}" rel="stylesheet">
<script src="{{ mix('/js/manifest.js') }}" defer></script>
<script src="{{ mix('/js/vendor.js') }}" defer></script>
<script src="{{ mix('/js/app.js') }}" defer></script>
@routes
</head>
<body>
<div id="app"
data-vue-component="{{ $name }}"
data-vue-props="{{ json_encode($data) }}"
data-csrf-token="{{ csrf_token() }}"
data-auth="{{ json_encode(['user' => Auth::user() ? Auth::user()->only('id', 'first_name', 'last_name') : null]) }}"
></div>
</body>
</html>
import Vue from 'vue'
import axios from 'axios'
const root = document.getElementById('app')
Vue.prototype.$http = axios
Vue.prototype.$http.defaults.headers.common['X-CSRF-TOKEN'] = root.dataset.csrfToken
Vue.prototype.$http.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
Vue.prototype.$route = route
const files = require.context('./', true, /\.vue$/i)
files.keys().map(key => Vue.component(key.split('/').slice(-1)[0].split('.')[0], files(key)))
new Vue({
render: h => h(Vue.component(root.dataset.vueComponent), { props: JSON.parse(root.dataset.vueProps) })
}).$mount(root)
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Illuminate\View\Factory as ViewFactory;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot()
{
ViewFactory::macro('component', function ($name, $viewData = [], $componentData = []) {
return View::make('app', ['name' => $name, 'data' => $componentData] + $viewData);
});
}
/**
* Register any application services.
*/
public function register()
{
}
}
<template>
<layout>
<h1>Create User</h1>
<form @submit.prevent="submit">
<text-input label="First name" v-model="form.first_name" :error="errors.get('first_name')"></text-input>
<text-input label="Last name" v-model="form.last_name" :error="errors.get('last_name')"></text-input>
<text-input label="Email" v-model="form.email" :error="errors.get('email')"></text-input>
<text-input label="Password" v-model="form.password" :error="errors.get('password')" type="password" autocomplete="new-password"></text-input>
<button type="submit" :disabled="sending">Create User</button>
</form>
</layout>
</template>
<script>
export default {
data() {
return {
form: {
first_name: null,
last_name: null,
email: null,
password: null,
},
sending: false,
errors: ErrorBag.new({}),
}
},
methods: {
submit() {
this.sending = true
axios.post(route('users.store'), this.form)
.then(response => window.location.href = route('users'))
.catch(error => {
this.sending = false
this.errors = ErrorBag.new(error.response.data.errors)
})
},
}
}
</script>
<template>
<div>
<header>
<img src="logo.png">
</header>
<nav>
<a :href="route('users')" :class="route().current('users*') ? 'active' : 'not-active'">Users</a>
<a :href="route('companies')" :class="route().current('companies*') ? 'active' : 'not-active'">Companies</a>
<a :href="route('products')" :class="route().current('products*') ? 'active' : 'not-active'">Products</a>
<a :href="route('settings')" :class="route().current('settings*') ? 'active' : 'not-active'">Settings</a>
</nav>
<main>
<slot></slot>
</main>
</div>
</template>
<?php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Request;
class UsersController extends Controller
{
public function index()
{
return View::component('Users', ['title' => 'Users'], [
'search' => Request::get('search'),
'users' => User::orderByName()
->search(Request::get('search'))
->paginate(15, ['id', 'first_name', 'last_name', 'email'])
->appends(Request::all()),
]);
}
public function create()
{
return View::component('CreateUser', ['title' => 'Create User']);
}
public function store()
{
$input = Request::validate([
'first_name' => ['required', 'max:50'],
'last_name' => ['required', 'max:50'],
'email' => ['required', 'max:50', 'email'],
'password' => ['nullable'],
]);
User::create($input);
}
public function edit(User $user)
{
return View::component('EditUser', ['title' => $user->name], [
'user' => $user->only('id', 'first_name', 'last_name', 'email'),
]);
}
public function update(User $user)
{
Request::validate([
'first_name' => ['required', 'max:50'],
'last_name' => ['required', 'max:50'],
'email' => ['required', 'max:50', 'email'],
'password' => ['nullable'],
]);
$user->update(Request::only(['first_name', 'last_name', 'email']));
if (Request::get('password')) {
$user->update(['password' => Request::get('password')]);
}
}
public function destroy(User $user)
{
$user->delete();
}
}
let mix = require('laravel-mix')
mix.js('resources/js/app.js', 'public/js')
.extract(['vue', 'lodash', 'axios'])
.webpackConfig({
resolve: {
alias: { 'vue$': 'vue/dist/vue.runtime.js' }
}
})
@yuri4n
Copy link

yuri4n commented Apr 23, 2020

This is brilliant, I think I am going to use this stack again, thanks

@reinink
Copy link
Author

reinink commented Apr 23, 2020

@JulianGarzon22 You might want to check out https://inertiajs.com. That's the progression of this idea. 👍

@CaduEspindola
Copy link

Is there any way to do this in Vue 3 + Vite?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment