<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
#Component
export default class Index extends Vue {
form = {
name: '',
adress: '',
age: '',
items: [],
}
async fetch() {
this.form.items = await fetch('https://api.nuxtjs.dev/posts').then(c => c.json())
console.log(this.form.items)
}
fetchOnServer = true;
changeLanguage(language: string) {
this.$i18n.setLocale(language);
}
}
</script>
async fetch dont work in class component
If I use a standard component it works fine. But if I use a class component, the function call stops working.
What is the problem and how can it be fixed
As an annotation argument it works
#Component({
components: {ValidationProvider, ValidationObserver},
async fetch() {
console.log('** fetch at ');
console.log(this.$route);
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
}
})
export default class xrayClient extends Vue {
As class methods created() works:
async created() {
console.log('** created ');
console.log(this.$route);
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
Mounted() works:
async mounted() {
console.log('** mounted ' + this.data);
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
}
Calling
async asyncData()
and
fetch() {
Don't run.
"nuxt": "^2.15.7",
Instead of vue-property-decorator, you should use nuxt-property-decorator.
Then you can specify your asyncData or fetch methods in the component options, like this:
<script lang="ts">
import { Vue, Component } from 'nuxt-property-decorator'
#Component({
async fetch() {
this.form.items = await fetch('https://api.nuxtjs.dev/posts').then(c => c.json())
console.log(this.form.items)
}
})
export default class Index extends Vue {
form = {
name: '',
adress: '',
age: '',
items: [],
}
changeLanguage(language: string) {
this.$i18n.setLocale(language);
}
}
</script>
See this GitHub issue comment for details: https://github.com/nuxt/nuxt.js/issues/5330#issuecomment-475595112
Related
I want to test if "onLogin" event emitted from child component will trigger "toLogin" function from parent correctly.
Login.vue
<template>
<ChildComponent
ref="child"
#onLogin="toLogin"
/>
</template>
<script>
import { useAuthStore } from "#/stores/AuthStore.js"; //import Pinia Store
import { userLogin } from "#/service/authService.js"; // import axios functions from another js file
import ChildComponent from "#/components/ChildComponent.vue";
export default {
name: "Login",
components: {
ChildComponent,
},
setup() {
const AuthStore = useAuthStore();
const toLogin = async (param) => {
try {
const res = await userLogin (param);
AuthStore.setTokens(res);
} catch (error) {
console.log(error);
}
};
}
</script>
login.spec.js
import { describe, it, expect, vi, beforeAll } from 'vitest';
import { shallowMount, flushPromises } from '#vue/test-utils';
import { createTestingPinia } from "#pinia/testing";
import Login from "#/views/user/Login.vue"
import { useAuthStore } from "#/stores/AuthStore.js";
describe('Login', () => {
let wrapper = null;
beforeAll(() => {
wrapper = shallowMount(Login, {
global: {
plugins: [createTestingPinia({ createSpy: vi.fn })],
},
});
})
it('login by emitted events', async () => {
const AuthStore = useAuthStore();
const loginParam = {
email: 'dummy#email.com',
password: '12345',
};
const spyOnLogin = vi.spyOn(wrapper.vm, 'toLogin');
const spyOnStore = vi.spyOn(AuthStore, 'setTokens');
await wrapper.vm.$refs.child.$emit('onLogin', loginParam);
await wrapper.vm.$nextTick();
await flushPromises();
expect(spyOnLogin).toHaveBeenCalledOnce(); // will not be called
expect(spyOnStore).toHaveBeenCalledOnce(); // will be called once
})
}
I expected both "spyOnLogin" and "spyOnStore" will be called once from emitted event, however, only "spyOnStore" will be called even though "spyOnStore" should only be called after "spyOnLogin" has been triggered.
The error message is:
AssertionError: expected "toLogin" to be called once
❯ src/components/__tests__:136:24
- Expected "1"
+ Received "0"
What do I fail to understand about Vitest & Vue-Test-Utils?
You shouldn't mock your toLogin method because its part of Login component which you are testing. Therefore, instead of expecting if toLogin has been called, you should check if instructions inside are working correctly.
In your case i would only test if after emit, userLogin and AuthStore.setTokens has been called.
I have only use Vue 2 with class components. Example:
#Options({
components: {
HelloWorld,
},
})
export default class Home extends Vue {}
However now that it is deprecated I am having issues access variables. I am not sure why I cannot use this anymore; I do not quite understand how the template will be aware of the variables and how the void methods can manipulate them.
</button>
{{name}}-{{age}}
env: {{ mode }} - My name: {{ myName}}
</div>
</template>
<script lang="ts">
import {api} from "#/lib/api";
export default {
name: "Home",
data() {
return {
name: String,
age: Number,
mode: process.env.NODE_ENV,
myName: process.env.VUE_APP_TITLE
}
},
methods: {
submit(): void {
api.getTest().then(
response => {
const testResponse = JSON.stringify(response)
this.name = JSON.parse(testResponse).name
this.age = parseInt(JSON.parse(testResponse).age)
}).catch(response => {
console.log("Error while getting the response", response)
})
},
counter(age: number): void {
age = age + 1
}
}
}
</script>
--- update 1 ----
I received some excellent advice from a poster, suggesting I ref or reactive.
Vue 3 is built with typescript which is why class components were decided to be deprecated. However I am not able to use my gRPC generated type objects, or at least I do not know how at this moment
IDE
axios
export const api = {
async getTest() {
try{
return await grpcClient.get<TestResponse>("/v1/test")
.then(res => {
console.log(url.baseUrl)
return res.data
})
}catch (err) {
console.log("error" + err);
}
},
}
So vue3 way of defining component is a bit different than v2 - more like native JS. Here's quick example how you component could look like in vue3. Instead of methods, just create function. Instead of data use reactive or ref.
import { reactive, computed } from 'vue'
import { api } from '#/lib/api'
export default {
setup() {
const state = reactive({
name: '',
age: 0,
mode: process.env.NODE_ENV,
myName: process.env.VUE_APP_TITLE
})
const submit = async () => {
try {
const response = await api.getTest()
state.name = response.name
state.age = response.age
} catch (error) {
console.log('Error while getting the response:', error)
}
}
const counter = (age) => {
state.age = age + 1
}
return {
...state,
submit
}
}
}
I am experimenting with Vue3's Composition API in a Laravel/VueJS/InertiaJS stack.
A practice that I have used a lot in Vue2 with this stack is to have 1 route that returns the Vue page component (eg. Invoices.vue) and then in the created() callback, I would trigger an axios call to an additional endpoint to fetch the actual data.
I am now trying to replicate a similar approach in Vue3 with composition API like so
export default {
components: {Loader, PageBase},
props: {
fetch_url: {
required: true,
type: String,
}
},
setup(props) {
const loading = ref(false)
const state = reactive({
invoices: getInvoices(),
selectedInvoices: [],
});
async function getInvoices() {
loading.value = true;
return await axios.get(props.fetch_url).then(response => {
return response.data.data;
}).finally(() => {
loading.value = false;
})
}
function handleSelectionChange(selection) {
state.selectedInvoices = selection;
}
return {
loading,
state,
handleSelectionChange,
}
}
}
This however keeps on giving me the propise, rather than the actual data that is returned.
Changing it like so does work:
export default {
components: {Loader, PageBase},
props: {
fetch_url: {
required: true,
type: String,
}
},
setup(props) {
const loading = ref(false)
const state = reactive({
invoices: [],
selectedInvoices: [],
});
axios.get(props.fetch_url).then(response => {
state.invoices = response.data.data;
}).finally(() => {
loading.value = false;
})
function handleSelectionChange(selection) {
state.selectedInvoices = selection;
}
return {
loading,
state,
handleSelectionChange,
}
}
}
I want to use function though, so I can re-use it for filtering etc.
Very curious to read how others are doing this.
I have been googling about it a bit, but cant seem to find relevant docu.
All feedback is highly welcomed.
I tried this now with async setup() and await getInvoices() and <Suspense> but it never displayed any content.
So this is how I'd do it, except I wouldn't and I'd use vuex and vuex-orm to store the invoices and fetch the state from the store.
<template>
<div>loading:{{ loading }}</div>
<div>state:{{ state }}</div>
</template>
<script>
import {defineComponent, ref, reactive} from "vue";
import axios from "axios";
export default defineComponent({
name: 'HelloWorld',
props: {
fetch_url: {
required: true,
type: String,
}
},
setup(props) {
const loading = ref(false)
const state = reactive({
invoices: []
})
async function getInvoices() {
loading.value = true;
await axios.get(props.fetch_url).then(response => {
state.invoices = response.data;
}).finally(() => {
loading.value = false;
})
}
return {
getInvoices,
loading,
state,
}
},
async created() {
await this.getInvoices()
}
})
</script>
<style scoped>
</style>
This is of course similar to what you're doing in option 2.
I'm using the composi api in my Vue project and the nuxt.js firebase module, I would like to call variables injected into modules, such as $ fireAuth, but I didn't find a solution.
Below is a small code training of how I would like it to work:
export default createComponent({
setup(_props, { root }) {
root.$fireAuth= ..
}
}
// or
export default createComponent({
setup(_props, { root , $fireAuth }) {
}
}
I have a work-around for this and it works! (For now.)
Create a dummy component (ex. AppFirebase.vue)
<template></template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
created() {
this.$emit("init", this.$fire);
},
});
</script>
Accessing NuxtFireInstance (ex. SomeComponent.vue)
<template>
<fire #init="initFB"></fire>
</template>
<script lang="ts">
import {
defineComponent,
reactive,
} from "#nuxtjs/composition-api";
import fire from "#/components/AppFirebase.vue";
export default defineComponent({
components: { fire },
setup() {
let _fire: any = reactive({});
const initFB = (fire: any) => {
_fire = fire;
};
const signout = async () => {
try {
await _fire.auth.signOut().then(() => {
// do something
});
} catch (error) {
console.log(error);
}
};
return {
initFB,
_fire,
signout,
};
},
});
</script>
Rickroll if you got it working!
Hi I'm currently trying to add users to a page using react native, redux, and firebase. When User 1 clicks join, they get added to the feed and likewise for other users. However, a problem I'm facing is when user 2 clicks join, they get added to the feed but don't get displayed on user 1's page unless the user 1 refocuses on the page after going away.
Here is my code for the page itself in react native
import React, { Component } from 'react';
import { Text, View, Button, TouchableOpacity, SafeAreaView, ScrollView, Image } from 'react-native';
import styles from '../styles.js'
import { connect } from 'react-redux'
import { FlatList } from 'react-native-gesture-handler';
import { FontAwesome5 } from '#expo/vector-icons';
import { Octicons } from '#expo/vector-icons';
import { FontAwesome } from '#expo/vector-icons';
import { addUser, removeUser, getLivingRoomUsers } from '../actions/livingRoomUser.js'
import { bindActionCreators } from 'redux'
class LivingRoom extends React.Component {
constructor(props) {
super(props);
this.state = {
inRoom: false,
isMuted: false
};
}
componentDidMount(){
this._unsubscribe = this.props.navigation.addListener('focus', () => {
this.props.getLivingRoomUsers()
});
}
joinRoom = () => {
this.props.addUser()
this.setState({ inRoom: true });
}
leaveRoom = () => {
this.props.removeUser(this.props.livingRoomUser)
this.setState({ inRoom: false });
}
...
render(){
return (
<View>
<SafeAreaView style={styles.livingRoomUserContainer}>
<FlatList
data={this.props.livingRoomUser.feed}
Here is my actions code for the redux portion:
export const addUser = () => {
return async (dispatch, getState) => {
try {
const { user } = getState()
const id = uuid.v4()
const livingRoomUser = {
id: id,
avatar: user.avatar,
username: user.username,
isMuted: false,
date: new Date().getTime(),
}
db.collection('livingroom').doc(id).set(livingRoomUser)
dispatch({
type: 'ADD_USER', payload: livingRoomUser
})
dispatch(getLivingRoomUsers())
} catch (e) {
alert(e)
}
}
}
export const removeUser = (livingRoomUser) => {
return async (dispatch, getState) => {
try {
db.collection('livingroom').doc(livingRoomUser.id).delete();
dispatch(getLivingRoomUsers())
//get living room users
} catch (e) {
alert(e)
}
}
}
export const getLivingRoomUsers = () => {
return async (dispatch, getState) => {
try {
const livingRoomUsers = await db.collection('livingroom').get()
let array = []
livingRoomUsers.forEach((livingRoomUser) => {
array.push(livingRoomUser.data())
})
dispatch({
type: 'GET_LIVING_ROOM_USERS', payload: orderBy(array, 'date', 'asc')
})
} catch (e) {
alert(e)
}
}
}
To summarize. I want the getUsers to be updated anytime someone adds/removes themself from the page. However, from my implementation currently actions only get updated for the current user and the feed only gets updated when the page is focused. How do I go about this?
use onSnapshot listener on the firestore then you can get the latest updates as the store change
export const getLivingRoomUsers = () => {
return async (dispatch, getState) => {
try {
db.collection('livingroom').onSnapshot(snapshot => {
let array = snapshot.docs.map(d => d.data());
dispatch({
type: 'GET_LIVING_ROOM_USERS',
payload: orderBy(array, 'date', 'asc'),
});
});
} catch (e) {
alert(e);
}
};
};