Redirecting users after an action - vue.js

I am trying to achieve having a redirection if the user logs in successfully. I was trying to call this.$router.push('/profile') with then the call back after login however I get an error stating Error Cannot read properties of undefined (reading '$router') I am not sure if there is a new flow to how to get this done since I am using composition with <script setup> syntax. I read the document reference online but still not seeing anything concrete as to how to do this type of navigation now. How can I achieve this with the new vue3 composition api? It seems I am missing something.

If you use setup() inside the script, you can't access the router with $router.
if you use vue3, this code can help you:
<script>
import { defineComponen } from 'vue'
import { useRouter } from 'vue-router'
export default defineComponent({
setup() {
const router = useRouter()
function login() {
router.push('/profile')
}
return {
login
}
}
})
</script>

Related

Vuex state getter not working inside the vue 3 composition api

Trying to implement the new Vue composition API I ran into an issue, my vuex state values are not working and it will return an $store undefined error. When I check if my language state is set it is set so theres data present. I could not find any good information about this but I did find some helper plugin but I dont want to use plugins for every issue so is there a way to do this?
I am using vue 3 with vuex 4
export default {
setup (props, context) {
console.log(context.root.$store.getters['i18n/language'])//not working
// more logic here
}
}
You should use the composable function called useStore to get the store instance :
import {useStore} from 'vuex'
export default {
setup (props, context) {
const store=useStore()
console.log(store.getters['i18n/language'])
}
}

How to get access to Vue.prototype in asyncData?

In plugin, I add new property to Vue prototype.
import Vue from 'vue';
import { DateTime } from 'luxon';
Object.defineProperty(Vue.prototype, '$DateTime', { value: DateTime });
I want to use it in asyncData, how to get access?
async asyncData(context) {
context.store.commit('calendar/setSelectDate', $DateTime.now());
}
I'm not sure that defineProperty is the way to go here. You could maybe inspect the Vue instance itself and see if there is some $DateTime on it, if it is, a context.$DateTime should do the trick.
If it's not working with context.$DateTime, maybe try it when your component is mounted in a regular this.$DateTime on a button click, for debugging purposes.
Using regular Nuxt plugins or even inject is a more clean and Vue-y way of doing things.
import { DateTime } from 'luxon'
export default ({ app }, inject) => {
inject('DateTime', DateTime)
}
then
async asyncData({ store, $DateTime }) {
store.commit('calendar/setSelectDate', $DateTime.now())
}
or from components/pages
this.$DateTime.now()
Speaking of Vue, you don't want to use vue-luxon. Looks like a simple and quick way of using the library. Is it because it is not mantained for already 7 months or something else?
import Vue from 'vue';
async asyncData(context) {
context.store.commit('calendar/setSelectDate', Vue.prototype.$DateTime.now());
}

What is an equivalent of `created()` in the Vue.js composition api?

Could you advise what is the equivalent of the created() in the new Vue composition API, which I'm using from within Vue2 like this:
import { reactive, toRefs } from '#vue/composition-api'
From the Composition API docs on Lifecycle Hooks:
Because setup is run around the beforeCreate and created lifecycle hooks, you do not need to explicitly define them. In other words, any code that would be written inside those hooks should be written directly in the setup function.
Anything you would have done in the created hook you can do in setup.
to help the community, the created() method can be used this way.
hope this helps :)
<script>
import TenantsAPI from "#/api/tenants";
export default {
setup() {
const tenantsAPI = new TenantsAPI(); //compositon api
};
}
//options api
//async created(){
// this.tenantsAPI = new TenantsAPI();
//}
</script>

Problem when importing js-cookies in Main.js

I'm trying import js-cookies in my main.js
Main.js
import * as Cookies from "js-cookie";
Vue.use(Cookies)
Using in component
this.$Cookies.set('name', data.user, { secure: true });
Error
TypeError: Cannot read property 'set' of undefined
what is the problem?
I have tried a thousand ways and it still does not work.
Vue.use(name) is used to install a vue plugin. The package will need an install method that receives a vue instance.
#1
You can use the cookies packages without a plugin importing the module in the component
<script>
import Cookies from 'js-cookie';
export default {
methods: {
addCookie() {
console.log('adding the cookie');
Cookies.set('chocolate', 'chookies');
console.log(Cookies.get());
}
}
}
</script>
#2 you can add a VUE plugin and set a Cookies prototype function to the Cookies module.
(Prototype vue functions will be available for components, it's standard to prefix them with $).
src/CookiesPlugin.js
import Cookies from 'js-cookie';
const CookiesPlugin = {
install(Vue, options) {
Vue.prototype.$Cookies = Cookies;
}
};
export default CookiesPlugin;
src/main.js
import CookiesPlugin from './CookiesPlugin';
Vue.use(CookiesPlugin);
In the component
this.$Cookies.set('chocolate', 'chookies');
console.log(this.$Cookies.get());
You are using a NOT Vue (Vanilla JS library) library and you are trying to use it as a Vue resource.
Try using this one instead

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