Vue TypeError: this is undefined - vue.js

Why this is undefined here? On logout click this is the error shown in the browser console TypeError: this is undefined
<script lang="ts">
import Vue from "vue";
import { getModule } from "vuex-module-decorators";
import Component from "vue-class-component";
import AuthModule from "#/store/auth";
import Store from "#/store";
const authModule = getModule(AuthModule, Store);
#Component({})
export default class App extends Vue {
mounted() {
console.log("App mounted");
}
onLogoutClick() {
authModule.logout().then(function() {
this.$router.push("/login");
});
}
}
</script>

try this.
methods: {
onLogoutClick() {
let self = this
authModule.logout().then(function() {
self.$router.push("/login");
});
}

Using an arrow function to the anonymous function solves this. As arrow functions bind this to the lexical scope's this (in this case onLogoutClick's this).
onLogoutClick() {
authModule.logout().then(() => {
this.$router.push("/login");
});
}

Related

Vue 3 vue-i18n. How to use $t(t) outside the app(.vue files)? Composition API

I want to format my date-time outside the component.
function transformDatetime(config, value) {
if (value) {
return $d(new Date(value), config);
}
return $t('-');
}
I'm trying to get $t from the App instance. But it only works in the context of the component, just like useI18n.
import { getCurrentInstance } from 'vue'
export default function useGlobal() {
const instance = getCurrentInstance()
if (!instance) return
return instance.appContext.config.globalProperties
}
I found a solution. Just import your i18n into a file outside the application and use i18n.global.t
import { createI18n } from "vue-i18n"
export const i18n = createI18n({
legacy: false,
locale: 'ja',
fallbackLocale: 'en',
messages,
})
import { i18n } from '#/i18n'
const { t: $t, d: $d, n: $n } = i18n.global
const expample = $t('some-text')
I think your method should also be a composable so you can simply use the method useI18n() inside.
Something like
use-transform-datetime.ts
import { useI18n } from 'vue-i18n'
export const useTransformDatetime = () => {
const { t } = useI18n()
const transformDatetime = (config, value) => {
if (value) {
return $d(new Date(value), config)
}
return t('-')
}
return {
transformDatetime
}
}
then simply use it in your vue file who need it
<script setup>
import { useTransformDatetime } from 'path-to-your-composables/use-transform-datetime'
const { transformDatetime } = useTransformDatetime()
console.log(transformDatetime(config, 'value'))
</script>
This way you can even add your others transform methods in the same composable
Try to use of the useI18n composable function from vue-i18n to get the t method instead of $t:
<script setup>
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
console.log(t('greeting'))
</script>

How to access $vuetify instance in setup function

Is there a way to get access to $vuetify (and any other added global) in the setup function?
Is there a way for composables to access it?
...
setup() {
const { isDesktop } = $vuetify.breakpoints.mdAndUp; // <=== how to get $vuetify
return { isDesktop };
},
Composable to get vuetify instance:
// useVuetify.ts
import { getCurrentInstance } from 'vue'
export function useVuetify() {
const instance = getCurrentInstance()
if (!instance) {
throw new Error(`useVuetify should be called in setup().`)
}
return instance.proxy.$vuetify
}
Import it in your component:
<!-- MyComponent.vue -->
<script lang="ts">
import { useVuetify } from './useVuetify'
import { computed } from 'vue'
/*...*/
setup() {
const vuetify = useVuetify()
const isDesktop = computed(()=>vuetify.breakpoints.mdAndUp)
return { isDesktop }
},
/*...*/
</script>
If you are using Vue <= 2.6.14 + #vue/composition-api instead of Vue 2.7, replace 'vue' with '#vue/composition-api'
As #Lagamura mentioned in the comments, this can be achieved with Vuetify 3 using useDisplay().
E.g.
const display = useDisplay();
console.log(display.mobile.value);

Vue warn]: Property "isMobileTerminal" was accessed during render but is not defined on instance

I'm working on a project that's both mobile and PC,I need to estimate the mobile terminal or PC terminal。
flexible.js
import { computed } from 'vue'
import { PC_DEVICE_WIDTH } from '../constants'
import { useWindowSize } from '#vueuse/core/index'
const { width } = useWindowSize()
// 判断当前是否为移动设备,判断依据屏幕宽度是否小于一个指定宽度(1280)
export const isMobileTerminal = computed(() => {
return width.value < PC_DEVICE_WIDTH
})
and the navigation/index.vue code is
<template>
<mobile-navigation v-if="isMobileTerminal"></mobile-navigation>
</template>
<script>
import { isMobileTerminal } from '../../../../utils/flexible'
import mobileNavigation from './mobile/index.vue'
export default {
name: 'index',
components: {
mobileNavigation
}
}
</script>
<style lang="scss" scoped></style>
My project catalog is shown below
isMobileTerminal is only imported in your component. It also needs to be made available to the template by declaring it in your component definition.
Returning it from the setup() hook is one way to do that:
<script>
import { isMobileTerminal } from '../../../../utils/flexible'
export default {
setup() {
return {
isMobileTerminal
}
}
}
</script>

calling SweetAlert2 inside Async method in VUE 3

I tried to pops out sweetalert if failed to retreive data from server
I've imported sweet alert in main.js :
import VueSweetalert2 from 'vue-sweetalert2'
import 'sweetalert2/dist/sweetalert2.min.css'
const app = createApp(App)
app.use(VueSweetalert2)
app.mount('#app')
And inside the Table.vue components i tried to call swal but got an error says (undefined $this.swal) instead :
<script>
import axios from 'axios'
import { onMounted, ref } from 'vue'
export default {
setup() {
let transactions = ref([])
onMounted(() => {
getTransactions()
})
async function getTransactions() {
try {
let { data } = await axios.get('http://127.0.0.1:8000/api/transactions')
transactions.value = data.data
} catch(e) {
this.$swal('Something went wrong.')
}
}
return {
transactions
}
}
}
</script>
Any suggestion how to solve this ?
You can't use this as the component instance inside setup() because the component has not been created yet. There are other ways to get that $swal property.
vue-sweetalert2 exposes SweetAlert via app.config.globalProperties.$swal or as a provide-ed $swal prop.
A simple way to use it in the Composition API is through inject():
import { inject } from 'vue'
export default {
setup() {
const swal = inject('$swal')
async function getTransactions() {
//...
swal('Something went wrong.')
}
}
}
demo 1
However, the vue-sweetalert2 docs recommend using sweetalert2 directly in this case:
When using "Vue3: Composition API" it is better not to use this wrapper. It is more practical to call sweetalert2 directly.
You can use sweetalert2 directly like this:
import { onMounted, inject } from 'vue'
import Swal from 'sweetalert2'
export default {
name: 'App',
setup() {
async function getTransactions() {
//...
Swal.fire('Something went wrong.')
}
onMounted(() => getTransactions())
}
}
demo 2
In main.js file
import VueSweetalert2 from 'vue-sweetalert2';
import 'sweetalert2/dist/sweetalert2.min.css';
const app = createApp(App);
app.use(VueSweetalert2);
window.Swal = app.config.globalProperties.$swal;
app.mount("#app");
Use Swal.fire() inside COMPOSITION API
export default {
setup() {
function yourFunctionName() {
Swal.fire('Hello !')
}
}
}

Cannot get Vuex state on async created

I have the following Vue component (based on the HelloWorld example)
<template>
<div class="hello">
<h1>Message: {{ post.title }} </h1>
<button #click="refreshPost">Refresh</button>
</div>
</template>
<script lang="ts">
import {Component, Vue, Prop, } from 'vue-property-decorator';
import {IPostModel} from "#/models/IPostModel";
import {namespace} from "vuex-class";
const postStore = namespace('Post')
#Component
export default class HelloWorld extends Vue {
#postStore.State
public post!: IPostModel
#postStore.Action
public refreshPost!: Promise<IPostModel>
async created(){
console.log("starting created");
await this.refreshPost;
console.log("finished created");
}
}
</script>
And the corresponding Vuex module
import axios from 'axios'
import { VuexModule, Module, Mutation, Action } from "vuex-module-decorators";
import { IPostModel } from "#/models/IPostModel";
#Module({namespaced: true })
class Post extends VuexModule{
public post: IPostModel = { id: -1, userId: -1, title: ''}
#Mutation
private setPost(newPost: IPostModel): void{
this.post = newPost;
}
#Action({rawError: true})
public async refreshPost(): Promise<IPostModel> {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
this.context.commit('setPost', response.data);
return response.data;
}
}
export default Post
The code to call the api works fine when I click the refresh button, but doesn't appear to set the state on the created hook and I can't figure out why. I've tried various combinations of Promise & await & async and it either errors or makes no difference.
You are missing some steps. Let's call the Vuex module, VuexModule
In your VuexModule, add a namespace name
#Module({ namespaced: true, name: 'Post' }) // add name
import { namespace } from 'vuex-class';
import { getModule } from 'vuex-module-decorators';
import VuexModule from '#/store/modules/vuexmodule.ts'; //module which has 'Post' as namespace
const postStore = namespace('Post')
let vuexModule: VuexModule;
export default class HelloWorld extends Vue {
async created() {
vuexModule = getModule(VuexModule, this.$store);
await vuexModule.refreshPost();
}
}
You can learn more in this excellent article