vue.js tutorial configurazione laravel

Laravel 11 e Vue.js è una combinazione ottimale per scrivere codice pulito ed efficiente.

In questo tutorial vediamo insieme come configurare e gestire il routing con Vue.js in laravel 11, le Vue Route.

Estimated reading time: 5 minuti

L’esempio descritto in questo articolo è disponibile su Github.

Per leggere e comprendere al pieno questo articolo si consiglia la lettura dell’articolo Laravel 11 e Vue.js: installazione, configurazione ed esempi, e implementazione del codice che puoi trovare su repository GitHUB.

Per implementare le routes in vue, prima cosa dobbiamo configurare il vue-router.

Installazione e abilitazione di vue-router

Innanzitutto, dobbiamo installare il pacchetto vue-router, e per far questo utilizziamo l’npm install:

npm install vue-router@latest

Successivamente procediamo a importare il vue-router direttamente in app.js:

resources/js/app.js:

import './bootstrap';
 
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router' 
import PostsIndex from './components/Posts/Index.vue'
 
createApp({})
    .component('PostsIndex', PostsIndex)
    .mount('#app')

Adesso, abbiamo bisogno di creare un elenco di routes, inizializzare il router e abilitarlo all’interno della createApp().

Inoltre, non abbiamo bisogno di definire i componenti nel createApp(), perchè già definiti nelle routes.

resources/js/app.js:

import './bootstrap';
 
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import PostsIndex from './components/Posts/Index.vue'
 
const routes = [ 
    { path: '/', component: PostsIndex },
] 
 
const router = createRouter({ 
    history: createWebHistory(),
    routes
}) 
 
createApp({})
    .use(router) 
    .mount('#app')

Vue Component per la seconda pagina

Ora, creiamo un componente Vue per la pagina di creazione del post e aggiungiamo un testo fittizio.

resources/js/components/Posts/Create.vue:

<template>    
     To-do.
</template>

Successivamente, possiamo creare un nuovo percorso e puntarlo a questo componente Vue.

resources/js/app.js:

import './bootstrap';
 
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import PostsIndex from './components/Posts/Index.vue'
import PostsCreate from './components/Posts/Create.vue' 
 
const routes = [
    { path: '/', component: PostsIndex },
    { path: '/posts/create', component: PostsCreate }, 
]
 
const router = createRouter({
    history: createWebHistory(),
    routes
})
 
createApp({})
    .use(router)
    .mount('#app')

Cambiare il layout: da Blade a Vue

Ma ora, non possiamo aggiungere nuovi link alla navigazione, sono nei file Laravel Blade di Breeze e non nei file Vue. Quindi, dobbiamo cambiare tutta la struttura per essere un file .vue, incluso il layout principale, in modo che saremmo in grado di utilizzare <router-link>.

In primo luogo, useremo il file resources/views/dashboard.blade.php come file HTML principale in cui abbiamo impostato id="app" nel Tag <body>, invece del precedente <div id="app">.

resources/views/dashboard.blade.php:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>{{ config('app.name', 'Laravel') }}</title>
 
    <!-- Fonts -->
    <link rel="preconnect" href="https://fonts.bunny.net">
    <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
 
    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="font-sans antialiased" id="app">
</body>
</html>

Successivamente, abbiamo bisogno di avere un componente root: il layout principale.

resources/js/layouts/App.vue:

<template>
    <div class="min-h-screen bg-gray-100">
        <nav class="bg-white border-b border-gray-100">
            <!-- Primary Navigation Menu -->
            <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
                <div class="flex justify-between h-16">
                    <div class="flex">
                        <!-- Logo -->
                        <div class="shrink-0 flex items-center">
                            <a href="/">
                                <svg viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg" class="block h-9 w-auto fill-current text-gray-800">
                                    <path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z"/>
                                </svg>
                            </a>
                        </div>
 
                        <!-- Navigation Links -->
                        <div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
                            <router-link to="/" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out">
                                Posts
                            </router-link>
                            <router-link to="/posts/create" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out">
                                Create Post
                            </router-link>
                        </div>
                    </div>
                </div>
            </div>
        </nav>
 
        <!-- Page Heading -->
        <header class="bg-white shadow">
            <div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
                <h2 class="font-semibold text-xl text-gray-800 leading-tight">
                    Dashboard
                </h2>
            </div>
        </header>
 
        <!-- Page Content -->
        <main>
            <div class="py-12">
                <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
                    <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                        <div class="p-6 bg-white border-b border-gray-200">
                            <router-view></router-view>
                        </div>
                    </div>
                </div>
            </div>
        </main>
    </div>
</template>

Sia <router-link> che <router-view> provengono da vue-router, sostituendo il tipico HTML <a href link. Quando qualcuno fa clic su quei link, non si aggiornerà l’intera pagina, ma solo la parte che si trova all’interno del router-view. Questo è il comportamento della SPA.

Il <router-view> è l’elemento dove tutti i principali contenuti dinamici saranno caricati.

Ora, dobbiamo caricare questo layout durante la creazione di un’app Vue.

resources/js/app.js:

import './bootstrap';
 
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './layouts/App.vue' 
import PostsIndex from './components/Posts/Index.vue'
import PostsCreate from './components/Posts/Create.vue'
 
// ...
 
createApp(App) 
    .use(router)
    .mount('#app')

Ora, navigando il progetto sul browser, possiamo vedere due link nella navigazione.

laravel vue nuova pagina
Nuova pagina

E se si fa clic su Create Post il componente Vue PostsCreate verrà attivato e l’URL nella barra cambierà.

Sottolinea il collegamento attivo

La sottolineatura sotto il link attivo è effettuata specificando le classi active-class all’interno del <router-link>.

<router-link to="/" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out">
    Posts
</router-link>

Prevenire il caricamento nel browser direttamente

Ora dobbiamo solo risolvere un problema.

Se provi ad accedere all’indirizzo /posts/create direttamente tramite URL, si otterrebbe un errore 404.

Per risolverlo, dobbiamo aggiungere un percorso all’app Laravel: per puntare qualsiasi percorso verso il file dashboard di visualizzazione.

routes/web.php:

Route::view('/{any?}', 'dashboard') 
    ->where('any', '.*'); 

Ora, navigando nelle pagine appena definite, il codice verrà caricato all’interno del Vue. Il prossimo passo sarà quello di effettuare una pulizia delle routes impostando Vue. Introdurremo la denominazione del percorso e sposteremo i percorsi in un file separato.

Diamo un nome alle routes

Aggiungiamo un nome alle routes.

resources/js/app.js:

import './bootstrap';
 
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './layouts/App.vue'
import PostsIndex from './components/Posts/Index.vue'
import PostsCreate from './components/Posts/Create.vue'
 
const routes = [
    {
        path: '/',
        name: 'posts.index', 
        component: PostsIndex
    },
    {
        path: '/posts/create',
        name: 'posts.create', 
        component: PostsCreate
    },
]
 
const router = createRouter({
    history: createWebHistory(),
    routes
})
 
createApp(App)
    .use(router)
    .mount('#app')

Nel layout principale dell’app, invece di codificare i link in modo statico, li andremo a legarli al nome, scrivendo <router-link :to="{ name: 'posts.create' }">

resources/js/layouts/App.vue:

<template>

// ...

<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
    <router-link :to="{ name: 'posts.index' }" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out"> 
        Posts
    </router-link> 
    <router-link :to="{ name: 'posts.create' }" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out"> 
        Create Post
    </router-link>
</div>

// ...

</template>

Definire i percorsi per nome ci dà flessibilità. Se decidiamo di cambiare il percorso, non avremmo bisogno di cambiare manualmente in ogni file in cui abbiamo usato quel percorso.

Percorsi in un file separato

Ora trasformiamo queste vie in un file separato. Crea un nuovo file con il codice sottostante.

resources/js/routes/index.js:

import { createRouter, createWebHistory } from 'vue-router';
 
import PostsIndex from '@/components/Posts/Index.vue'
import PostsCreate from '@/components/Posts/Create.vue'
 
const routes = [
    {
        path: '/',
        name: 'posts.index',
        component: PostsIndex
    },
    {
        path: '/posts/create',
        name: 'posts.create',
        component: PostsCreate
    },
]
 
export default createRouter({
    history: createWebHistory(),
    routes
})

Per poter importare questo file di routes nel main app.js, andiamo a usare l’export default.

E ora possiamo importare questo file di routes all’interno del main app.js. Il file app.js diventa come segue:

resources/js/app.js:

import './bootstrap';
 
import { createApp } from 'vue'
import App from './layouts/App.vue'
 
import router from './routes/index' 
 
createApp(App)
    .use(router)
    .mount('#app')

Ora puoi visitare la pagina, e dovrebbe funzionare come previsto. Non abbiamo cambiato nulla visivamente, ma abbiamo aggiunto più ordine nel nostro codice per le implementazioni successive.

Vediamo ora come possiamo creare variabili globali. Ora, in ogni pagina, abbiamo un intestazione con la scritta Dashboard. Proviamo a modificarla in modo dinamico.

Ci sono alcuni modi per farlo. Passeremo quel testo come parametro. Dentro il file App.vue andiamo a cambiare la parola Dashboard con un attributo variabile.

resources/js/layouts/App.vue:

<template>
// ...
<!-- Page Heading -->
<header class="bg-white shadow">
    <div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ currentPageTitle }} 
        </h2>
    </div>
</header>
// ...
</template>

Noi renderemo questa variabile una proprietà calcolata, elaborata.

resources/js/layouts/App.vue:

<template>
// ...
</template>
 
<script setup>
import { computed } from 'vue';
 
const currentPageTitle = computed(() => 'New computed dashboard')
</script>

Ora impostiamo dinamicamente il testo per ogni pagina. Per questo aggiungeremo una nuova variabile meta.

resources/js/routes/index.js:

// ...
const routes = [
    {
        path: '/',
        name: 'posts.index',
        component: PostsIndex,
        meta: { title: 'Posts' } 
    },
    {
        path: '/posts/create',
        name: 'posts.create',
        component: PostsCreate,
        meta: { title: 'Add new post' } 
    },
]
// ...

Per accedere a questa meta variabile nel layout principale dell’app, dobbiamo importare percorsi Composable e usarli per ottenere il meta con il titolo.

resources/js/layouts/App.vue:

<template>
// ...
</template>
 
<script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router' 
 
const route = useRoute() 
  
const currentPageTitle = computed(() => route.meta.title) 
</script>

Navigando nelle pagine del progetto potrai vedere l’esito delle modifiche fatte, e iniziale a caricare i primi post.

L’esempio descritto in questo articolo è disponibile su Github.

Creazione nuovo post vue e laravel
Inserimento nuovo Post con Vue Laravel

Letture Correlate

Ercole Palmeri

Autore