vue3 i18n, how to combine Vue Router + i18n? - internationalization

I would like to combine i18n with the Vue Router (vue3). I can setup the i18n module successfully but the integration into the routing system always fails.
The router-view is located in App.vue:
<template>
<div class="container">
<!-- Content here -->
<the-header></the-header>
<router-view></router-view>
</div>
</template>
<script>
import TheHeader from './components/TheHeader.vue'
export default {
name: 'App',
components: {
TheHeader
}
}
</script>
I access the language routes via a global object $t. The languager switcher works. So the following router-links in the TheHeader component contain the right paths to the language specific components (this.$i18n.locale always returns the right path-fragment: eg: 'en','de' etc.., this works!!):
<ul class="navbar-nav">
<li class="nav-item">
<router-link class="nav-link active" aria-current="page"
:to="`/${this.$i18n.locale}/home`">{{ $t('nav.home') }}</router-link>
</li>
<li class="nav-item">
<router-link class="nav-link" :to="`/${this.$i18n.locale}/about`">{{ $t(`nav.about`) }}
</router-link>
</li>
Now I stuck with the router. I found the following example here, but it does not work:
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/:lang",
component: {
render: (h) => h("router-view"),
},
children: [
{
path: "home",
name: "home",
component: Home,
},
{
path: "design",
name: "design",
component: Design,
},
{
path: "about",
name: "about",
component: About,
},
{
path: "contact",
name: "contact",
component: Contact,
},
],
},
],
});
stack trace:
Uncaught (in promise) TypeError: h is not a function
at Proxy.render (router.js?41cb:15)
at renderComponentRoot (runtime-core.esm-bundler.js?5c40:464)
at ReactiveEffect.componentUpdateFn [as fn] (runtime-core.esm-bundler.js?5c40:4332)
at ReactiveEffect.run (reactivity.esm-bundler.js?a1e9:160)
at setupRenderEffect (runtime-core.esm-bundler.js?5c40:4458)
at mountComponent (runtime-core.esm-bundler.js?5c40:4241)
at processComponent (runtime-core.esm-bundler.js?5c40:4199)
at patch (runtime-core.esm-bundler.js?5c40:3791)
at ReactiveEffect.componentUpdateFn [as fn] (runtime-core.esm-bundler.js?5c40:4409)
at ReactiveEffect.run (reactivity.esm-bundler.js?a1e9:160)
Principally, the language switcher should work independently from the router, with the independent global variable $t. However, I need the complete path in the URL, I need an integration of i18n into the router! How can I configure the Vue Router correctly?

import { h, resolveComponent } from "vue";
path: "/:lang",
component: {
render() {
return h(resolveComponent("router-view"));
},
},

Related

Laravel 8 Vue Package Could Not Find a Declaration Module

I'm trying to create a table view with laravel framework using jetstream inertia js vue package, and came across revogrid npm package, so i installed it using the vue 3 version.
Installation went fine, but when i imported the component, it gives me Could not find a declaration module for '#revolist/vue3-datagrid' and after npm run dev no changes happened.
my container.vue :
<template>
<app-layout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Customer
</h2>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
<v-grid
theme="compact"
:source="rows"
:columns="columns"
></v-grid>
</div>
</div>
</div>
</app-layout>
</template>
<script>
import AppLayout from '#/Layouts/AppLayout'
export default {
data: function () {
return {
columns: [{ prop: "name" }, { prop: "details" }],
rows: [{
name: "1",
details: "Item 1",
}]
}
},
components: {
AppLayout,
VGrid
},
methods: {
fetchCustomer: function () {
axios.get('/api/customers')
.then(response => this.rows = response.data)
.catch(error => console.log(error))
}
},
created: function () {
this.fetchCustomer()
}
}
</script>
error :
error message
result is empty page, while expected result is a sample table

Dynamic Name Routing / Dynamic Route Matching in VueJS

I have some products that are being iterated over and being displayed. I'd like to use the images of the products as links to the specific page of each individual product. I want each product page to pull from the same template, substituting the props with the appropriate product details.
An example url for a product would be something like: /shop/product/name-of-product
Here is the relevant code:
<template>
<div class="p-avaible" v-for="item in avaibleProducts" :key="item.name">
<router-link :to={ name: 'avaibleProducts' , params: { id: 1 }}>
<img :key="item.image" :src="item.image">
</router-link>
<div class="p-name">{{ item.name }}</div>
<div class="p-price">€{{ item.price }}</div>
<div class="btn-container">
<button class="add-to-cart">ADD TO CART</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
cart: [],
avaibleProducts: [
{
name: "PLASTIC BAGS 3-PACK v1",
price: 0.33,
image: require('#/assets/plastic-bag-pack.jpg'),
description: 'First version plastic bags pack containing 3 HQ assets. Make sure to get yours today.',
id: 1
},
{
name: "VINYL TEXTURES 2-PACK v1",
price: 0.22,
image: require('#/assets/vinyl-texture-pack.jpg'),
description: 'First version vinyl texture pack containing 2 HQ assets. Make sure to get yours today.',
id: 2
},
{
name: "STICKER PACK 6-PACK v1",
price: 0.66,
image: require('#/assets/sticker-bag-pack.jpg'),
description: 'First version sticker bag pack containing 6 HQ assets. Make sure to get yours today.',
id: 3
}
],
};
}
};
</script>
Router/Index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Shop from '../views/Shop.vue'
import Product from '../views/Product'
Vue.use(VueRouter)
const routes = [
{
path: '/shop',
name: 'Shop',
component: Shop
},
{
path: '/product/:id',
name: Product,
component: Product
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
hey :)) first you must add a unique property to your product list like a uuid or anything, also you can use id property but its not a good method
step 1:
you must add uuid propery to your product object :
{
name: 'PLASTIC BAGS 3-PACK v1',
price: 0.33,
image: require('#/assets/plastic-bag-pack.jpg'),
description:
'First version plastic bags pack containing 3 HQ assets. Make sure to get yours today.',
id: 1,
uuid: 'prd-001' // pay attention to this line
},
step 2:
you need to create an computed propery
computed: {
showProduct() {
const id = this.$route.params.id;
const product = this.avaibleProducts.find((p) => p.uuid == id);
return product;
},
step 3:
and in your template you can access it like this:
<ul>
<li>{{ showProduct.name }} - {{ showProduct.price }} <!-- and etc ... {{ showProduct.image }} --></li>
</ul>
step 4:
you can load single product in this route:
/product/prd-001
the above route return your first product in your available products state
step 5:
change your this line in your Router/Index.js file
name: Product
and put it in single quotation like this :
name: 'Product'
and change your router-link like this :
<router-link :to="{name: 'Product' , params:{ id: product.uuid }}">{{ product.name}}</router-link>
well done!
You're missing one important part in your router index.js file. You need to enable prop passing in the Product components
Router/Index.js
{
path: '/product/:id',
name: Product,
component: Product,
props: true
}
Now you can actually pass props to your routes via a <router-link> element.
Now all you have to do is pass the appropriate props to the component. You can do this by emulating an API call on the component's created() hook.
I recommend you make a JSON file that you put somewhere in your src directory with all the details for the products. Instead of importing your image via webpack, just do it statically by putting the images in public/images.
You then need to make sure that the id is a unique URL-valid string if you want to use it as the param in the URL as you specified. It would look something like this:
#/assets/json/productList.json:
[
{
"id": "plastic-bag",
"name": "PLASTIC BAGS 3-PACK v1",
"price": 0.33,
"image": "/images/products/1.jpg",
"description": "First version plastic bags pack containing 3 HQ assets. Make sure to get yours today."
},
....
]
Then in your Product.vue component:
<template>
<div>
<img :src="product.image" alt="">
<h1>{{ product.name }}</h1>
<pre>${{ product.price }} USD</pre>
<p>{{ product.description }}</p>
</div>
</template>
<script>
import products from "#/assets/json/productList.json";
export default {
data() {
return {
product: null;
};
},
created() {
this.setProduct();
},
methods: {
setProduct() {
const currentProject = products.find(project => project.id === this.$route.params.id); // Find project via the route id param
this.product = currentProject;
}
}
};
</script>

Vue - Vue routes doesn´t exist

I have a project in Laravel + Vue. In Laravel i have some routes for create endpoints and start page.
Laravel Routes
Route::get('/', 'Auth\LoginController#showLoginForm');
Route::post('/login', 'Auth\LoginController#login');
Auth::routes();
Route::resource('gateways', 'GatewayController');
Route::resource('contadores', 'ContadorController');
'/' route go to Blade file with Login Component.
Login Component has this code.
<template>
<v-content slot="content">
<v-container class="fill-height" fluid>
<v-row align="center" justify="center">
<v-col cols="12" md="8">
<v-card class="elevation-12">
<v-toolbar dark flat>
<v-toolbar-title>LoRaWAN</v-toolbar-title>
</v-toolbar>
<v-card-text>
<v-form>
<v-text-field
label="Usuario"
name="username"
prepend-icon="mdi-account"
type="text"
v-model="username"
/>
<v-text-field
label="Contraseña"
name="password"
prepend-icon="mdi-key"
:append-icon="value ? 'mdi-eye' : 'mdi-eye-off'"
#click:append="() => (value = !value)"
:type="value ? 'password' : 'text'"
v-model="password"
/>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn block dark #click="submit()">Entrar</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</v-container>
</v-content>
</template>
<script>
export default {
data() {
return {
value: String,
username: "",
password: ""
};
},
methods: {
submit() {
axios
.post("http://127.0.0.1:8000/login", {
username: this.username,
password: this.password
})
.then(response => {
if (response.data.token != null) {
localStorage.setItem("token", response.data.token);
console.log("ok");
this.$router.push({
name: "lora",
params: { user: this.username }
});
}
})
.catch(function(errors) {
let error = errors.response.data.errors;
let mensaje = "Error no identificado";
if (error.hasOwnProperty("username")) {
mensaje = error.username[0];
} else {
mensaje = error.password[0];
}
Swal.fire({
title: "Error",
text: mensaje,
icon: "error",
confirmButtonText: "Ok"
});
});
}
}
};
</script>
As we can see when login endpoint return token we want to push to other 'lora' route.
Vue routes file
import ContadorComponent from "./components/contador/ContadorComponent.vue";
import GatewayComponent from "./components/gateway/GatewayComponent.vue";
import HomeComponent from "./components/home/HomeComponent.vue";
import MainComponent from "./components/main/MainComponent.vue";
const routes = [{
path: "/lora",
name: "lora",
component: MainComponent,
props: true,
children: [{
path: "",
name: "home",
component: HomeComponent
},
{
path: "contadores",
name: "contadores",
component: ContadorComponent
},
{
path: "gateways",
name: "gateways",
component: GatewayComponent
}
]
}];
const router = new VueRouter({
mode: 'history',
routes: routes
});
new Vue({
vuetify: new Vuetify(),
router
}).$mount("#app");
And lora route (Main Component)
<template>
<v-app id="app">
<layoutDrawer></layoutDrawer>
<layoutHeader></layoutHeader>
<v-content>
<router-view></router-view>
</v-content>
<layoutFooter></layoutFooter>
</v-app>
</template>
<script>
import layoutHeader from "./partials/HeaderComponent.vue";
import layoutFooter from "./partials/FooterComponent.vue";
import layoutDrawer from "./partials/SidebarComponent.vue";
export default {
props: {
username: { type: String, default: "Invitado" }
},
components: {
layoutHeader,
layoutDrawer,
layoutFooter
}
};
</script>
The problem: If i go to http://127.0.0.1:8000/lora returns that this route doesn´t exist. In the vue routes file i declare it, so i don´t know why returns this. Maybe Laravel generate a conflict or something with routes. In laravel routes file i test this code and works
Route::get('/test', function () {
return view('home');
})->name('home');
The view home is blade file with Main Component. Maybe something happens with the vue routes that project doesn't recognize and only works Laravel routes..
The question: Are the vue routes properly declares? Anybody see some error?
Your client and server are running on the same port: http://127.0.0.1:8000.
The url for your lora route should be something like http://127.0.0.1:8001/lora
I found a partially solution. In Laravel routes i need to put this
Route::get('{any?}', function () {
return view('layout');
})->where('any', '.*');
Every time the user push to another page load Layout blade.
#extends('layouts.app')
#section('content')
<layout-component></layout-component>
#endsection
Layout Component
<template>
<v-app id="app">
<router-view></router-view>
</v-app>
</template>

Routing through different components in vue

I am currently working with laravel and vuejs for a booking application, the flow of the app is that once the user clicks to book they get redirected to a page where the booking process starts, this page is where i instantiate vue, and i also call the first component (Book Component) to be rendered:
#section('content')
{{-- <div id="mute" class="on"></div> --}}
<div style="padding: 0.9rem" id="app">
{{-- <router-view name="bookBus"></router-view> --}}
<booker :dref="{{ $route }}" :user="{{ Auth::user()->id }}"></booker>
<router-view></router-view>
</div>
#stop
#section('scripts')
my app.js file looks like this:
// Import Components
import BookComponent from './components/BookComponent.vue';
import ConfirmComponent from './components/ConfirmComponent.vue';
import PayComponent from './components/PayComponent.vue';
Vue.component('booker',BookComponent);
const routes = [
{
path: '/',
component: BookComponent,
name: 'bookBus',
meta: {
mdata: model,
userId: userId
},
},
{
path: '/confirm/:bookId',
component: ConfirmComponent,
name: 'confirmBook',
},
{
path: 'payment/:bookRef',
component: PayComponent,
name: 'payNow',
}
]
const router = new VueRouter({
routes,
mode: 'history',
});
const app = new Vue(
{
router,
}).$mount('#app')
after this the next component to be rendered is the confirmation component that asks the user to confirm the submitted details, and after this is the final payment component. The issue is once the booking component has been processed successfully i programmatically moved to the confirm component. But the confirm component renders directly below the book component. what i want to achieve is for the confirm component to render alone and not below the book component.
#section('content')
{{-- <div id="mute" class="on"></div> --}}
<div style="padding: 0.9rem" id="app">
{{-- <router-view name="bookBus"></router-view> --}}
<router-view></router-view>
</div>
#stop
#section('scripts')
app.js
// Import Components
import BookComponent from './components/BookComponent.vue';
import ConfirmComponent from './components/ConfirmComponent.vue';
import PayComponent from './components/PayComponent.vue';
const routes = [
{
path: '/',
component: BookComponent,
name: 'bookBus',
meta: {
mdata: model,
userId: userId
}
},
{
path: '/confirm/:bookId',
component: ConfirmComponent,
name: 'confirmBook',
},
{
path: 'payment/:bookRef',
component: PayComponent,
name: 'payNow',
},
{
path: '*',
redirect: '/'
}
]

How can I prevent Vue template from reloading when loading a Laravel view

I am quite new to Laravel and Vue and I'm currently building an app and trying to incorporate Vue to handle my sidebar navigation items.
Currently my sidebar is populating as expected from my view template. Everything is working great and when I first launch my app, navigation item 0 is selected and highlighted. However, when I click on any other navigation item, I can see the active class being added briefly to that item, but then the new Url loads and my vue template is getting reloaded making navigation item 0 once again active.
I would have thought that only my #yeild( 'content' ) should be reloading and anything sitting in my main blade template should remain unchanged.
Which makes me think I have overlooked something somewhere or have something wrong.
Main blade template:
<body>
<div class="main-wrapper">
<!-- Header -->
#include( 'layouts.header' )
<!-- Sidebar -->
#include( 'layouts.sidebar' )
<!-- Content Wrapper. Contains page content -->
<div #auth class="content-wrapper" #else class="content-wrapper-no_sidebar" #endauth>
<div class="container-fluid">
<!-- Main content -->
<section class="content">
#yield('content')
</section><!-- /.content -->
</div>
</div><!-- /.content-wrapper -->
</div>
...
</body>
layouts.sidebar:
<div id="side_bar">
#auth
<aside class="left-sidebar">
<nav>
<side-bar></side-bar>
</nav>
</aside>
#endauth
</div>
SideMenu.vue (side-bar template file):
<template>
<div class="sidebar-menu">
<a v-for="( item, index ) in items" class="side-nav-item" :class="{ active: item.active }" :href="item.Url" #click="toggleItem( index )">
<i :class="item.Icon"></i><br>
{{ item.Description }}
</a>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{
active: false,
Description: 'Dashboard',
Icon: 'fas fa-tachometer-alt',
Url: '/home'
},
{
active: false,
Description: 'Test Cases',
Icon: 'fas fa-calendar-check',
Url: '/testcases'
},
{
active: false,
Description: 'Applications',
Icon: 'fab fa-windows',
Url: '/applications'
},
{
active: false,
Description: 'Testers',
Icon: 'fas fa-users',
Url: '/testers'
},
{
active: false,
Description: 'Modules',
Icon: 'fas fa-sitemap',
Url: '/categories'
}
],
currentActiveIndex: 0,
}
},
methods: {
toggleItem( index ) {
this.items[this.currentActiveIndex].active = false;
this.items[index].active = true;
this.currentActiveIndex = index;
}
},
mounted() {
this.items[this.currentActiveIndex].active = true;
console.log( 'Sidebar Loaded' );
}
}
</script>
app.js:
Vue.component( 'side-bar', require( './components/SideMenu.vue' ) );
// Define new vue instances
const side_bar = new Vue({
el: '#side_bar'
});

Resources