Use I18n in url - vue.js

i have this url in tempalte but it doesn't works:
<a :href="`/${i18n.locale}'/profiles/'${teammate.profile.id}`" target="_blank">Hi</a>
my i18n file
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
let index = {}
index[window.I18n_locale] = window.I18n
// Ready translated locale messages
const messages = index
// Create VueI18n instance with options
export default new VueI18n({
locale: window.I18n_locale, // set locale
messages, // set locale messages
})
gives me this error in console:
Property or method "i18n" is not defined on the instance but
referenced during render. Make sure that this property is reactive,
either in the data option, or for class-based components, by
initializing the property.
how should i write it to make it works?

The variable name should be $i18n.
<a :href="`/${$i18n.locale}/profiles/${teammate.profile.id}`" target="_blank">Hi</a>
From the Vue18n documentation:
Each component contains a VueI18n instance referenced as the $i18n property...

Related

How to correctly set up Cypress 10, Vue2, Vuetify, Composition API for component testing?

The guide is quite confusing and obviously not correct when trying to set up Cypress 10 for component testing with Vue2 and Vuetify with composition API. There's lots of errors of unknown tags, things returned from setup() aren't accessible, spread operators where there shouldn't be, imports that don't work etc. What's the correct way to set things up so testing works?
You need to set up Vuetify as regular, to the global Vue object. Then in the mount you need to give the Vuetify object to the mount function so it can be found by the components. When using Composition API that also needs to be set up regularly to the global instance (unlike Vuetify it also works in the local instance, if you want).
Then mount the component inside a v-appso it should work properly and pass arugments around.
So component.ts file will include this:
import { mount } from 'cypress/vue2'
import Vuetify from 'vuetify'
import VueCompositionAPI from '#vue/composition-api';
import Vue from 'vue'
import { VApp } from 'vuetify/lib/components/VApp';
Vue.use(Vuetify);
Vue.use(VueCompositionAPI);
Cypress.Commands.add('mount', (component, args) => {
args.vuetify = new Vuetify(yourVuetifyOptions);
return mount({ render: (h) => h(VApp, [h(component, args)]) }, args);
})
When using the mount just do:
cy.mount(myComponent, { props: {someProp: 123 } });
If you need to set up plugins for the local Vue instance in the test they need to be set in args.extensions.plugins, the guide seems to mention globals but that is incorrect.
cy.mount(myComponent, { props: {someProp: 123 }, extensions: { plugins: [MyPlugin] } });
Note that I'm using args for both settings parameters for mount and also for the component, if needed those two can be separated. But there shouldn't be much clashing of properties and attributes so this works.
Also the props/attributes/etc for the component must be given as they're given to createElement, not mount (so props instead of propsData etc).

Pass single-spa prop to i18n instantiation

is there a way to pass a single-spa prop to a vue i18n instance, assigning it to the messages i18n prop.
I18n constructor:
const i18n = new VueI18n({
locale: process.env.VUE_APP_I18N_LOCALE,
messages: {},
});
Vue single-spa instance:
const vueLifecycles = singleSpaVue({
Vue,
appOptions: {
el: '#account',
render() {
return (
<App
eventBus={this.$data.eventBus}
lang={this.$data.lang}
></App>
);
},
i18n,
},
});
Info: I dont have access to lang outside the render function.
Basically, I need the lang prop inside my i18n messages object. I tried already to return the lang and but it into the constructor, doesnt work.
I also tried to reinstantiate i18n/messages after lang is received, doesnt work either.
Any other ideas?
So I figured it out. It is only possible to alter the messages in your mounted vue instance with:
this.$i18n.setLocaleMessage = newLangObj

Using Moment.js as a plugin inside main.js

I registered Moment.js as a plugin, like this:
import Vue from 'vue'
import moment from 'moment'
moment.locale('pt_BR')
Vue.use({
install (Vue) {
Vue.prototype.$moment = moment
}
})
Now, I need to use this in my main.js filters
import './plugins/moment'
Vue.filter('formatDate', value => {
return this.$moment(value, 'YYYY-MM-DD').format('DD/MM/YYYY')
})
Buth this return an error:
Error in render: "TypeError: Cannot read property '$moment' of
undefined"
Looks like you can not access this like in the vue components for filter methods.
By Evan You
This is intentional in 2.x. Filters should be pure functions and should not be dependent on this context. If you need this you should use a computed property or just a method e.g. $translate(foo)
I guess the best way is importing the moment on main.js like this:
import moment from 'moment'
Vue.filter('formatDate', value => {
return moment(value, 'YYYY-MM-DD').format('DD/MM/YYYY')
})
Vue.filter() creates a global filter before the Vue instance is created, a global filter does not have access to the Vue instance therefore you can not access this.$moment. If you need to access the vue instance, you'll need to register a local filter on components.
However, you could rectify this by directly referencing the module:
import moment from 'moment';
Vue.filter('formatDate', value => {
moment(value, 'YYYY-MM-DD').format('DD/MM/YYYY')
});

Creating a single instance of a class within a Vue application

I'm new to Vue and I'm struggling to wrap my head around how to implement what seems to me like a good case for a global variable or singleton.
The background is that I'm using Azure AD B2C for authentication with the MSAL library. MSAL requires a single instance of the Msal.UserAgentApplication to be declared and then shared through the application.
What I'm struggling with is how to declare that instance somewhere central and then access it from each component including the router.
At the moment I've got a class which is similar to this example: https://github.com/sunilbandla/vue-msal-sample/blob/master/src/services/auth.service.js and when I want to use the methods I'm doing:
var authService = new AuthService();
authService.Login();
Unfortunately this creates a new instance of MSAL each time the class is instantiated which in turn caused my users to end up stuck in an authentication loop.
Any help would be greatly appreciated.
Many thanks.
Following on from the answer below by Teddy I've amended my main.js as follows:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './registerServiceWorker'
import AuthService from './services/AuthService';
Vue.config.productionTip = false
Vue.prototype.$authService = new AuthService();
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app');
And my register.vue component as follows:
<template>
<div class="about">
<h1>This is the register page, it should redirect off to B2C</h1>
</div>
</template>
<script>
import router from '#/router.js'
export default {
created(){
this.$authService.isAuthenticated().then(
function(result){
if(result){
router.push('/');
}
else{
authService.register();
}
});
}
}
</script>
The component is saying that this.$authService is undefined so it's obviously not reading the prototype.
It feels like I'm missing something really fundamental in Vue at this point.
You can just add it as a Vue instance property. It will be there for all Vue components.
Set it up in main.js like this:
Vue.prototype.$authService = new AuthService();
You can later access it in any Vue component. For example:
this.$authService.Login();
Refer:
https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html
Edit:
You have to use this.$router.push and this.$authService.register inside the isAuthenticated callback. If "this" refers to something else in that block, store var self=this; before the callback starts, or use fat arrow syntax.
<script>
//No import as router is available in 'this'
export default {
created(){
var self=this; //For use inside the callback
this.$authService.isAuthenticated().then(
function(result){
if(result){
self.$router.push('/');
}
else{
self.$authService.register();
}
});
}
}
</script>
Edit 2:
Maybe you can create the instance (singleton) itself in a file called AuthServiceInst.js. Then you can import it in both main.js and router.js.
New file AuthServiceInst.js:
import AuthService from './AuthService.js'
export const authService = new AuthService();
main.js:
import {authService} from './AuthServiceInst.js'
Vue.prototype.$authService = authService;
router.js:
import {authService} from './AuthServiceInst.js'
//Now you can use authService
In Vue 3, to declare global instances you need to use app.config.globalProperties. This is a replacement of Vue 2's Vue.prototype which is no longer present in Vue 3. As with anything global, this should be used sparingly.
// main.js
const app = createApp(App)
.use(router)
.use(store)
.use(vuetify)
app.config.globalProperties.msg = 'hello world'
app.mount('#app')
This makes msg available inside any component template in the application, and also on this of any component instance:
export default {
mounted() {
console.log(this.msg) // 'hello world'
}
}
Source: Docs

vue.js 2 how use components in ES2015 webpack

I am trying to use vue-components in a webpack Typescript project but it doesn't seem to be working. I don't get any errors during the build and run, but the component HTML is never inserted into the output - I can just see the HTML source of the component instead i.e. .
My project is an ES2015 using Vue2 in VS.Net 2017. My component looks like this:
import Vue from 'vue'
import Component from 'vue-class-component'
// The #Component decorator indicates the class is a Vue component
#Component({
// All component options are allowed in here
template: '<button #click="onClick">Click!</button>'
})
export default class MyHeader extends Vue {
// Initial data can be declared as instance properties
message: string = 'Hello!'
// Component methods can be declared as instance methods
onClick(): void {
window.alert(this.message)
}
}
I have tried the official reference guide to register the component and use it. When I look at the vue-component example, it uses the same format as my project so I added the markup and properties to my Typescript class definition:
import Vue from 'vue';
import Component from 'vue-class-component';
import MyHeader from './MyHeader';
#Component({
components: {
MyHeader
}
})
export default class GetDataComponent extends Vue {
<...rest of class...>
}
but in my project the "components:" section is squiggly-underline-red with the message:
Object literal may only specify known properties, but 'components'
does not exist in type 'VueClass'. Did you mean to write
'component'?
Every example I have seen with vue-component (such as this one) uses the "components:" option in the #Component to register and use their Vue component, but in my project it doesn't seem to like it. I have also tried global registration of the component (such as this one) which includes the line:
// Register the component globally
Vue.component(my-header', MyHeader)`
but in that case I get an error like this:
Type 'typeof MyHeader' is not assignable to type 'AsyncComponent'
The Vue file works (without the Component added) and all content is rendered correctly. It's getting the Component included that doesn't work - I either get Design-time errors per above, or nothing appears in the output at all.
Is my import wrong? Or the format of the #Component? I get the feeling I am doing something that is very basic, very wrong...