I'm relatively comfortable with Laravel 5 but a rather green when it comes to Vue.js and I was wandering if it is possible to call Laravel's methods which are available from blade in a Vue.js component.
For example I want to make a Vue.js component for my navbar like so:
<template>
<nav class="navbar navbar-expand-sm navbar-dark bg-info mb-2">
<div class="container">
<a href="{{ route('home') }}" class="navbar-brand">Go Home</a>
</div>
</nav>
</template>
<script>
export default {
name: "NavBar"
}
</script>
home
is a named route in my wep.php
file
Route::get('/', 'SiteController@index')
->name('home');
and I added my Vue component like so:
Vue.component('navbar', require('./components/NavBar'));
in my app.js
file
How can I get something like this done? In this example I only use the route()
method to insert a src
tag but I am also interested in doing something analogous to this from a Vue.js component
@if (Route::has('login'))
<div class="top-right links">
@auth
<a href="{{ url('/home') }}">Home</a>
@else
<a href="{{ route('login') }}">Login</a>
<a href="{{ route('register') }}">Register</a>
@endauth
</div>
@endif
This is how I would do it from my *.blade.php
file but how can I do this type of logic using Vue components? I'm using Laravel 5.7 by the way.
I have been building Vue Apps with Laravel without any blade templates other than an index to present the base HTML meta data required for the SPA to run.
Beyond that, My Apps typically require an authenticated user throughout and so on user authenticated login request I return with the user success response all the settings and permissions relevant to that user.
To get started and populate Vue with all available web routes, add the following to routes/web.php
Route::get('/js/routes.js', function() {
// White list namespaces that can be exposed to Vue.
$allow = ['App\Http\Controllers'];
// uncomment below to enable cache
// $routes = \Illuminate\Support\Facades\Cache::rememberForever('routes.js', function () {
$routes = [];
foreach(Route::getRoutes()->getIterator() AS $route)
{
if(!array_key_exists('namespace',$route->action))
continue;
if(!is_array($route->action['middleware']))
continue;
if(in_array($route->action['namespace'],$allow,true))
continue;
$name = $route->getName();
if(empty($name))
continue;
$routes[$name] = $route->uri;
};
return $routes;
// });
header('Content-Type: text/javascript');
echo 'let routes = ' . json_encode($routes) . ';';
echo 'window.route = function() { let args = Array.prototype.slice.call(arguments); let name = args.shift();if(routes[name] === undefined) { console.error(\'Unknown route \', name); } else { return \'/\' + routes[name].split(\'/\').map(s => s[0] == \'{\' ? args.shift() : s).join(\'/\');}};';
exit();
})->name('assets.routes');
Add to your blade layout file:
<link rel="preload" href="/js/routes.js" as="script">
To then make those routes available in Vue components you can bind to Vue using the following example after import Vue from 'Vue':
Vue.prototype.$getRoute = window.route;
Vue['getRoute'] = window.route;
You can then in Vue methods use this.getRoute('laravels.route.name', [ id: user.id ])
, this works much like the route function in Laravel with url variables passed as the second parameter. In a router link component or click event it would work like so:
<router-link :to="$getRoute('logout')">Logout</router-link>
<button @click="$getRoute('logout')">Logout</button>
The value passed in getRoute is the name of the route in Laravel when you use Route::get(...)->name('my.custom.route.name')
in web.php
Use command php artisan route:list
to list all route names of Resources you have created that you can use for $getRoute() in Vue.
On a side note as an extra added bonus, you can return custom JSON data for a newly authenticated user by editing Auth/LoginController.php and adding the following method:
public function authenticated(Request $request, $user)
{
return response()->json([
'redirect' => session()->pull('url.intended')
]);
}
You can expand on this and return many addition credentials. For starters, I am passing back an intended redirect path that actually belongs to Vue router. The Vue application has already pushed the user to the login screen but Laravel also has a snapshot in session of where the user was going before this.$router.push('login') occurred.
Vue.prototype.$Laravel = window.Laravel;
Vue['Laravel'] = window.Laravel;
logMeIn() {
this.$root.isAuthenticating = true;
axios.post(this.$getRoute('login.send'),this.input).then(response => {
this.Laravel.myArray = response.data.myArray;
this.$router.push(response.data.redirect);
}).catch(error => {
this.$root.isAuthenticating = false;
ErrorHandler(this,error);
});
},