Vue-Router and i18n with Nuxt - vue.js

I am transferring a project with a vue-router from Vue.js to Nuxt.js.
After hours of trying and googleing I managed to get everything to work but this one issue that I cant get to work: **Integrating i18n and vue-router with Nuxt.js **
Two problems I could not solve yet:
How to properly update i18n.locale in my router.js file?
How to tell nuxt-i18n that I am using my own router? It now obviously complains about every page that the Route with name 'XXX___en' does not exist.
In my plain vue project the following code works, and I can change the locale in router.js and it would propagate and update this.$i18n.
i18n.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import termsHTML from './translations/terms'
import privacyHTML from './translations/privacy'
import imprintHTML from './translations/imprint'
Vue.use(VueI18n)
export const i18n = new VueI18n({
locale: 'en',
fallbackLocale: 'en',
messages: { ... }
}
router.js
...
import {
i18n
} from "#/plugins/i18n.js";
...
router.beforeEach((to, from, next) => {
// Set locale to new language
// I CANT GET THIS NEXT LINE WORING IN NUXT.JS
if (i18n.locale !== lang) i18n.locale = lang
...
}
Now in Nuxt I want to do the same. I imported the nuxt-i18n plugin like this in nuxt.config.js:
['nuxt-i18n', {
seo: true,
locales: [{
code: 'en',
name: '🇺🇸',
iso: 'en-US'
},
{
code: 'de',
name: '🇩🇪',
iso: 'de-DE'
}],
defaultLocale: 'en',
vueI18n: {
locale: 'en',
fallbackLocale: 'en',
messages: translations
}
}],
I tried the following in my router without success.
router.js
import nuxtConfig from "~/nuxt.config";
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
// access the i18n module which is the second item in the modules array
// then take second item which is the config object and take the vueI18n property
let i18n = nuxtConfig.modules[1][1].vueI18n
...
if (i18n.locale !== lang) i18n.locale = lang
This does not error, but does also not work. Vue.use() is probably wrong here, as this registers a new component, I guess my code does not refer to the right i18n?
Thanks for any help

I now got the thing I wanted by doing the redirects not in the router file but in the NavBar.vue component which gets loaded allways. There you can access this.$i18n without a problem.

It works for me:
{
path: '/:lang([a-z]{0\,2})/blog/authors',
component: AuthorsList
},

Related

Is there a way to import a folder structure in Vite

I have a bunch of sub-folders plus a few .vue components in the src/pages folder. With webpack I was able to get a list of the page paths and name with code like this:
export default require
.context("../pages", true, /^\.\/.*\.vue$/)
.keys()
.map(page => page.slice(2).replace(".vue", ""))
.filter(page => page !== "Index")
.map(page => ({
file: page,
title: createTitle(page),
path: slugify(kebabCase(page))
}));
Vite doesn't seem to support this. I tried const pages = import.meta.glob('../pages/*.vue') but this only works for files, not files inside sub-folders.
Any idea how I can achieve this with Vite?
I found a way. It's not perfect but not terrible either:
const pages = import.meta.glob('../pages/*.vue')
const folders = import.meta.glob('../pages/*/*.vue')
const both = {...pages, ...folders}
export default both
This is a refinement:
const pages = import.meta.glob('../pages/**/*.vue')
export default pages
I think you're looking for something like vite-plugin-pages :
installation :
npm install -D vite-plugin-pages
which requires vue-router to be installed :
npm install vue-router
Add to your vite.config.js:
import Pages from 'vite-plugin-pages'
export default {
plugins: [
// ...
Pages(),
],
}
Router config :
import { createRouter } from 'vue-router'
import routes from '~pages'
const router = createRouter({
// ...
routes,
})
There're also other plugin like unplugin-vue-components to resolve components and vite-plugin-vue-layouts to resolve layouts

Using Stencil components with Ionic Vue

In the Stencil docs section on framework integration with Vue it states the following:
In order to use the custom element library within the Vue app, the
application must be modified to define the custom elements and to
inform the Vue compiler which elements to ignore during compilation.
According to the same page this can be achieved by modifying the config of your Vue instance like this:
Vue.config.ignoredElements = [/test-\w*/];
This relates to Vue 2 however. With Vue 3 (which Ionic Vue uses) you have to use isCustomElement as stated here.
Regretably, I can’t for the life of me get Vue and Stencil to play nice. I have tried setting the config like this:
app.config.compilerOptions.isCustomElement = tag => /gc-\w*/.test(tag)
This causes Vue throw the following warning in the console:
[Vue warn]: The `compilerOptions` config option is only respected when using a build of Vue.js that includes the runtime compiler (aka "full build"). Since you are using the runtime-only build, `compilerOptions` must be passed to `#vue/compiler-dom` in the build setup instead.
- For vue-loader: pass it via vue-loader's `compilerOptions` loader option.
- For vue-cli: see https://cli.vuejs.org/guide/webpack.html#modifying-options-of-a-loader
- For vite: pass it via #vitejs/plugin-vue options. See https://github.com/vitejs/vite/tree/main/p
However, I have no idea how to implement any of the above suggestions using Ionic Vue. I have been messing around with chainWebpack in config.vue.js but without success so far.
Any help would be greatly appreciated.
I'm not an expert in Vue but here's how I did it:
Add the following to your ./vue.config.js (or create it if it doesn't exist):
/**
* #type {import('#vue/cli-service').ProjectOptions}
*/
module.exports = {
// ignore Stencil web components
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
options.compilerOptions = {
...options.compilerOptions,
isCustomElement: tag => tag.startsWith('test-')
}
return options
})
},
}
This will instruct Vue to ignore the test-* components. Source: https://v3.vuejs.org/guide/web-components.html#skipping-component-resolution
Next, load the components in ./src/main.ts.
Import the Stencil project:
import { applyPolyfills, defineCustomElements } from 'test-components/loader';
Then replace createApp(App).use(router).mount('#app') with:
const app = createApp(App).use(router);
// Bind the custom elements to the window object
applyPolyfills().then(() => {
defineCustomElements();
});
app.mount('#app')
Source: https://stenciljs.com/docs/vue
Also, if anyone is using vite2+, just edit the vite.config.js accordingly:
import { fileURLToPath, URL } from 'url'
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue({
template: {
compilerOptions: {
isCustomElement: tag => tag.startsWith('test-') // ✅ Here
}
}
}) ],
resolve: {
alias: {
'#': fileURLToPath(new URL('./src', import.meta.url))
}
}
})

getting error when trying to upgrade to vuetify 2.0

Ok so I am trying for the second time to migrate thus far it has been a complete failure it seems that vuetify is not detected, unfortunately I cannot share my full repo since it is work related, but will describe steps and share relevant code.
Project was created with vue-cli 3.3.0 with a vue.config.js file for environment variables.
1) npm uninstall vuetify
2)vue add vuetify
3)npm run serve
my site does not load and I get this error (adding code):
//vue.config.js
module.exports = {
chainWebpack: (config) => {
config.plugin('define')
.tap(([options, ...args]) => {
let env = options['process.env'].VUE_APP_ENV.replace(/"/g,'');
let envMdl = require('./build/' + env.toString() + '.js');
// replace all current by VUE concrente ones to be passed to the app
const processEnv = Object.assign({}, options['process.env'])
Object.keys(envMdl).forEach(function (k) {
processEnv['VUE_APP_' + k] = envMdl[k];
});
const ret = Object.assign({}, options, {'process.env': processEnv});
return [
ret,
...args
]
})
}
}
//vuetify.js
import Vue from 'vue'
import Vuetify from 'vuetify/lib'
Vue.use(Vuetify)
export default new Vuetify({
icons: {
iconfont: 'mdiSvg',
},
})
//main.js
import vuetify from './plugins/vuetify'
...
new Vue({
vuetify,
router,
store,
i18n,
render: h => h(App),
...
Error message (and screenshot): Uncaught TypeError: _lib.default is not a constructor
at eval (vuetify.js?402c:6)
The main problem is that Vuetify v1 works under the Stylus preprocessor, and in v2 it works under the SASS preprocessor, and I personally do not recommend migrating to v2 if it is too advanced and even worse if it has custom Vuetify components.

plugin is not defined in instance.vue

I struggle to add a plugin in Nuxt.js. I have been looking to the doc and all kind of similar problems, but I got the same error: simpleParallax is not defined.
I tried different approach on all files
nuxt.config.js:
plugins: [
{src: '~/plugins/simple-parallax.js', mode:'client', ssr: false}
],
plugins/simple-parallax.js:
import Vue from 'vue';
import simpleParallax from 'simple-parallax-js';
Vue.use(new simpleParallax);
index.vue:
Export default {
plugins: ['#/plugins/simple-parallax.js'],
mounted() {
var image = document.getElementsByClassName('hero');
new simpleParallax(image, {
scale: 1.8
});
}
}
Error message:
ReferenceError: simpleParallax is not defined.
The best solution I found out so far is to register simpleParallax on the Vue prototype like so in a plugin nuxt file with the name simple-parallax.client.js:
import Vue from 'vue';
import simpleParallax from 'simple-parallax-js';
Vue.prototype.$simpleParallax = simpleParallax;
Also my nuxt.config.js file if anyone would like to verify that as well:
plugins: [
{src: '~/plugins/simple-parallax.client.js', mode: 'client', ssr: false}
],
I then have access to the plugin before instantiation in my case in the mounted life cycle of the primary or root component to grab the desired HTML elements and instantiate their individual parallax with the newly added global method this.$simpleParallax
For example I can then intiate a certain HTML element to have its parallax like so:
const someHTMLElement = document.querySelectorAll('.my-html-element');
const options = {...} // your desired parallax options
new this.$simpleParallax(someHTMLElement, options);
Actually you don't need to use plugin here.
Just import simpleParallax from 'simple-parallax-js' in your component and init it with your image in mounted hook.
index.vue:
import simpleParallax from 'simple-parallax-js'
export default {
...
mounted() {
// make sure this runs on client-side only
if (process.client) {
var image = document.getElementsByClassName('thumbnail')
new simpleParallax(image)
}
},
...
}
And don't forget to remove previously created plugin, it's redundant here.

Vue.js migration to 2.0: Failed to mount component: template or render function not defined. (found in root instance)

// Edit: I think i have the problem solved. i needed the standalone build from vue...
i have migrated my vue.js 1.0 app to vue.js 2.0 (with migration helper). But this error shows me in the console Failed to mount component: template or render function not defined. (found in root instance).
Here is my simplified main.js:
import Vue from 'vue';
import Router from 'vue-router';
import SiteHeader from './components/Header.vue';
import Content from './components/Content/Content.vue';
import SiteFooter from './components/Footer.vue';
Vue.use(Router);
const router = new Router({
mode: 'history',
root: '/',
routes: [
{ path: '/', component: Content }
]
});
new Vue({
router,
components: {
SiteHeader, SiteFooter
}
}).$mount('body');
The Content.vue is an normal vue file with template and script (nothing special here).
My ´router-view` is defined in an laravel.blade file
#if(Request::is('login'))
<login></login>
#else
<site-header></site-header>
<router-view></router-view>
<site-footer></site-footer>
#endif
For starters, you are not allowed to mount to the body element in Vue 2.0.
Also, as the error message states, you have not provided a template or render function.
I assume (as you did not show) you have template markup directly in the HTML page.
This can work, but only with the "standalone" version of Vue. However, the default export of Vue's npm package is the "runtime build", which can't parse such templade code.
Further information here:
https://vuejs.org/guide/installation.html#Standalone-vs-Runtime-only-Build