Vue Composition Api - watch property imported from external composition function - vue.js

I have external composition function where i declare property like this:
export default function useDescription() {
const description = ref('');
return {
description
}
}
Then, I would like to import this property in other component, inside the setup method and watch for the changes like this:
setup() {
const { description } = useDescription();
watch(description, (value) => {
//do sth
})
}
Unfortunately it does not work.

Remove the ref property outside the function in order to be accessible from both components :
import { ref, watch } from "vue";
const description = ref("");
export default function useDescription() {
return { description };
}
and
setup() {
const { description } = useDescription();
watch(()=>description, (value) => {
//do sth
})
}

Related

Trying to change change page title, using Quasar, meta and vue-i18n

I am using Quasar v2, using the Vue Composition API and vue-i18n, and I would like the site title to change display when the active language changes (via a drop down), but whatever I am trying does not result in the title language being changed. Any ideas?
Below is what I have right now (just the essentials):
import { defineComponent, ref, computed } from 'vue';
import { useMeta } from 'quasar';
export default defineComponent({
setup () {
const { t: translate } = useI18n() as any;
const siteTitle = computed(() => translate('title.app') as string);
const pageMetadata = {
title: 'untitled',
titleTemplate: (title: string) => `${title} - ${siteTitle.value}`
};
useMeta(pageMetadata);
}
});
The code I am using to switch languages:
async onChangeLanguage () {
try {
let locale = this.language;
if (this.language === 'en') {
locale = 'en-GB';
}
this.$i18n.locale = locale;
const quasarLang = await import(`quasar/lang/${locale}`);
if (quasarLang) {
Quasar.lang.set(quasarLang.default);
}
} catch (error) {
this.$log.error(error);
}
}
According to the documentation, useMeta will not be reactive if you pass a simple object to it. Rather, you should pass a function that returns the desired value:
export default defineComponent({
setup () {
const { t: translate } = useI18n() as any;
const siteTitle = computed(() => translate('title.app') as string);
useMeta(() => {
const title = 'untitled';
const titleTemplate = `${title} - ${siteTitle.value}`
return { title, titleTemplate }
});
});

Why is 'route.params.id' unavailable/undefined in setup method?

I'm working with vue-router and Vue 3. I have a view where I'd like to take the router url and use it to call a method to access an API. This method returns a promise I can use to populate my page. When calling my method with 'route.params.id', it says that the parameter is undefined. When I do console.log(route.params.id), it displays correctly in console. I've tried using a computed property instead, but I had the same issue.
Setup code:
import { ref } from "vue";
import MovieApiService from "../api/MovieApiService";
import { useRoute } from "vue-router";
export default {
setup() {
const movie = ref([]);
const route = useRoute();
MovieApiService.getMovie(route.params.id).then((response) => {
movie.value = response.data.results;
});
return {
movie,
};
},
method: {},
};
Method being called:
static getMovie(body: GetMovieByTmdbId) {
return axios.get(
`https://api.themoviedb.org/3/movie/${body.id}?api_key=${apiKey}`
);
}
Here's what I tried to compute the property instead, with the same result.
setup() {
const route = useRoute();
const id = computed(()=>{return route.params.id})
const movie = ref([]);
getMovie(id).then((response) => {
movie.value = response.data.results;
console.log(movie.value);
});
How should I ensure this value is available when I call my method?
You are giving route.params.id as a variable to getMovie(body).
You are then using body.id in your URL. This would equal route.params.id.id which is not defined.
Use body in your URL, or change the parameter to id so is makes more sense.
Like this:
...
const route = useRoute();
MovieApiService.getMovie(route.params.id).then((response) => {
movie.value = response.data.results;
});
...
const getMovie = (id) => {
return axios.get(
`https://api.themoviedb.org/3/movie/${id}?api_key=${apiKey}`
);
}

Returning Apollo useQuery result from inside a function in Vue 3 composition api

I'm having some issues finding a clean way of returning results from inside a method to my template using Apollo v4 and Vue 3 composition API.
Here's my component:
export default {
components: {
AssetCreationForm,
MainLayout,
HeaderLinks,
LoadingButton,
DialogModal
},
setup() {
const showNewAssetModal = ref(false);
const onSubmitAsset = (asset) => {
// how do I access result outside the handler function
const { result } = useQuery(gql`
query getAssets {
assets {
id
name
symbol
slug
logo
}
}
`)
};
}
return {
showNewAssetModal,
onSubmitAsset,
}
},
}
The onSubmitAsset is called when user clicks on a button on the page.
How do I return useQuery result from the setup function to be able to access it in the template? (I don't want to copy the value)
You can move the useQuery() outside of the submit method, as shown in the docs. And if you'd like to defer the query fetching until the submit method is called, you can disable the auto-start by passing enabled:false as an option (3rd argument of useQuery):
export default {
setup() {
const fetchEnabled = ref(false)
const { result } = useQuery(gql`...`, null, { enabled: fetchEnabled })
const onSubmitAsset = (asset) => {
fetchEnabled.value = true
}
return { result, onSubmitAsset }
}
}
demo

v-model and Composition API with provide and inject

I would like to know how can I show the value from composition API with v-model and Composition API.
Currently I have my store.js :
import { reactive, toRefs, computed } from "vue";
export default function users() {
// State
const state = reactive({
userForm: null,
});
// Mutations
const UPDATE_USER_FORM = (user) => {
state.userForm = user;
};
// Actions
const updateUserForm = (payload) => {
UPDATE_USER_FORM(payload);
};
// Getters
let getUserForm = computed(() => state.userForm);
return {
...toRefs(state),
updateUserForm,
getUserForm
}
}
I provide my store in createApp :
import users from '#/Stores/users';
...
let myApp = createApp({ render: () => h(app, props) });
myApp.provide('userStore', users());
I inject my store in my component :
setup(props, context) {
const userStore = inject('userStore');
return { userStore }
}
In the template I use it, but I don't see the value :
I try this :
<div>userForm : {{userStore.userForm}}</div> // see the user object
<div>userForm with value : {{userStore.userForm.value.firstname}}</div> // see the firstname value
<div>userForm no value : {{userStore.userForm.firstname}}</div> // don't see the firstname
<input v-model="userStore.userForm.firstname"> // don't see the firstname
I would like to use the value in the input...
First thing that you should do is to put the state outside the composable function in order to be available for all components as one instance :
import { reactive, toRefs, computed } from "vue";
// State
const state = reactive({
userForm: null,
});
export default function users() {
// Mutations
...
return {
state,
updateUserForm,
getUserForm
}
}
second thing is to import the composable function in any component you want since the inject/provide could have some reactivity issues :
<input v-model="state.userForm.firstname">
...
import users from './store/users'
....
setup(props, context) {
const {state,updateUserForm,getUserForm} = users();
return { state }
}

Vue Composition API reactivity doesn't work properly

I am using Vue2, Vuetify, Vue Composition API(#vue/composition-api)
The problem I faced is that composition api reactivity doesn't work properly.
Let me show you some code
---- companies.vue ----
<template>
...
<v-data-table
:headers="companiesHeaders"
:items="companies"
:loading="loadingCompanies"
/>
...
</template>
<script>
...
import { useCompanies } from '#/use/companies'
export default {
setup: (_, props) => {
...
const {
companies,
loadingCompanies,
getCompanies
} = useCompanies(context)
onMounted(getCompanies)
return {
...,
companies,
loadingCompanies
}
}
}
</script>
---- #/use/companies.ts ----
import { ref } from '#vue/composition-api'
export const useCompanies = (context: any) => {
const { emit, root } = context
const companies = ref([])
const loadingCompanies = ref(false)
const getCompanies = async () => {
if (loadingCompanies.value) { return }
try {
loadingCompanies.value = true
companies.value = (await root.$repositories
.companies.getCompanies()).data
console.log(companies.value)
// This log works properly. It logs company list once received
// But even after this async function is finished, companies and loadingCompanies are not updated automatically
} catch (err) {} finally {
loadingCompanies.value = false
}
}
return {
companies,
loadingCompanies
}
}
I tried with both ref and reactive.
But reactivity for whatever inside companies.vue doesn't work.
I resolved the issue.
The issue was that company variable instance was created in 2 places(one for create company dialog and one for table), so changes in one place(create company dialog) didn't affect to the other(table).
Thanks.