I am trying to create a multi step form with composition api.
In vue 2 I used to do it this way
email: {
get() {
return this.$store.state.email
},
set(value) {
this.$store.commit("setEmail", value)
}
},
Now I have my own store, I made this computed property to pass to my component stEmail: computed(() => state.email). How can I actually use this in get set?
I am trying do something like this but completely doesn't work.
let setMail = computed(({
get() {
return stEmail;
},
set(val) {
stEmail.value = val;
}
}))
const state = reactive({
email: "",
})
export function useGlobal() {
return {
...toRefs(state),
number,
}
}
Or is there better way now to make multi step forms?
You can do the same with the Composition API. Import useStore from the vuex package and computed from vue:
import { computed } from 'vue';
import { useStore } from 'vuex';
And then use it in your setup() function like this:
setup: () => {
const store = useStore();
const email = computed({
get() {
return store.state.email;
},
set(value) {
store.commit("setEmail", value);
}
});
return { email };
}
If you want to avoid using vuex, you can just define variables with ref() and export them in a regular JavaScript file. This would make your state reusable in multiple files.
state.js
export const email = ref('initial#value');
Form1.vue/Form2.vue
<template>
<input v-model="email" />
</template>
<script>
import { email } from './state';
export default {
setup() {
return { email };
}
};
</script>
As Gregor pointed out, the accepted answer included an anonymous function that doesn't seem to work, but it will work if you just get rid of that part. Here's an example using <script setup> SFC
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
const email = computed({
get() {
return store.state.email
},
set(value) {
store.commit("setEmail", value)
}
})
</script>
<template>
<input type="email" v-model="email" />
</template>
Related
<template>
<div>
<h1>Vuex Typescript Test</h1>
<button #click="handleLogin">click</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from '#vue/composition-api'
export default defineComponent({
setup() {
return {
handleLogin() {
// something....
},
}
},
})
</script>
#vue/composition-api do not apply useStore
I want to use store in setup function.
You should be able to access the useStore composable in the setup function according to the documentation of Vuex.
Your script section will look like this:
import { defineComponent } from '#vue/composition-api';
import { useStore } from 'vuex';
export default defineComponent({
setup() {
return {
const store = useStore();
return {
handleLogin {
store.dispatch('auth/login');
},
};
}
},
});
The proper way to structure the content of setup would be to move the handleLogin as a separate const and expose the constant in the return, in order to keep the return section more readable like this:
setup() {
const store = useStore();
const handleLogin = () => {
store.dispatch('auth/login');
};
return {
handleLogin,
}
}
How to create reactive() object with custom setter (debounced) like with customRef()
You can use computed inside reactive. Namely, you might want to use a Writable Computed (getter/setter):
import { ref, reactive, computed } from 'vue'
const whatever = ref('test')
const state = reactive({
myProp: computed({
get() {
console.log('myProp was read');
return whatever.value
},
set(val) {
console.log(`myProp was set to ${val}`)
whatever.value = val
}
})
})
Test:
const { createApp, ref, reactive, computed, toRefs } = Vue
const app = createApp({
setup() {
const whatever = ref('test')
const state = reactive({
myProp: computed({
get() {
console.log('myProp was read');
return whatever.value
},
set(val) {
console.log(`myProp was set to ${val}`)
whatever.value = val
}
}),
emitter: computed({
get() {
return true
},
set(val) {
console.log(`I was set to ${val} but I'm not changing value...`)
}
})
})
return { ...toRefs(state), whatever }
}
})
app.mount('#app')
<script src="https://unpkg.com/vue#3.2.41/dist/vue.global.prod.js"></script>
<div id="app">
<input v-model="myProp" />
<pre v-text="{
whatever,
myProp,
emitter
}"></pre>
<button #click="emitter = !emitter">Click me!</button>
</div>
Notice the getter is only called once per change, not once for each place where it's used in <template>.
I have a problem in a component.
I receive an id (name : theIdPost) from a parent file of this component but when I would like to use it in the mounted(){} part , it tells me :
TS2339: Property 'theIdPost' does not exist on type '{...
I can print the id in template, no worries but to use it in the SCRIPT part it doesn't work.
the component file:
<template lang="fr">
// All my html
</template>
<script lang="ts">
import { computed } from 'vue';
import { store } from '../store/index';
export default{
name: 'comment',
props: {
theIdPost: Number,
theTxtPost: String,
theLike: Number,
},
setup() {
const myStore: any = store
const commentList = computed(() => myStore.state.commentList);
console.log("CommentList > " +commentList.value);
return { commentList };
},
mounted() {
const myStore: any = store;
myStore.dispatch("getComments",
{'id': this.theIdPost}
);
}
}
</script>
<style lang="scss">
#import "../scss/variables.scss";
// ..... the style part
</style>
Can you explain me why it doesn't work ?
Thanks
If you are using the composition API with the setup, you have to add the lifecycle hooks differently:
https://v3.vuejs.org/guide/composition-api-lifecycle-hooks.html
setup(props) {
const myStore: any = store
const commentList = computed(() => myStore.state.commentList);
console.log("CommentList > " +commentList.value);
onMounted(() => {
myStore.dispatch("getComments",
{'id': props.theIdPost}
);
})
return { commentList };
},
For Solution there is 2 points :
because I use vue 3 and setup in composition API , the lifecycle Hook is different and mounted => onMounted
setup(props) {
const myStore: any = store
const commentList = computed(() => myStore.state.commentList);
onMounted(() => {
myStore.dispatch("getComments",
{'id': props.theIdPost}
);
})
return { commentList };
},
when we use onMounted, is like when we use ref(), we have to import before. So at the beginning of the SCRIPT part, we have to write :
import { onMounted } from 'vue';
So my final script is :
<script lang="ts">
import { computed, onMounted } from 'vue';
import { store } from '../store/index';
export default {
name: 'comment',
props: {
theIdPost: Number,
theTxtPost: String,
theLike: Number,
},
setup(props) {
const myStore: any = store;
const commentList = computed(() => myStore.state.commentList);
onMounted(() => {
myStore.dispatch("getComments",
{ 'id': props.theIdPost }
);
})
return { commentList };
},
}
</script>
Thanks to Thomas for the beginning of the answer :)
it worked for me too. i was setting up the setup and not pass props in to the setup. now okay
I am a bit confused with composition API and fetching data. When I open the page, I can see rendered list of categories, but if I want to use categories in setup(), it is undefined. How can I use categories value inside setup function? You can see that I want to console log categories.
Category.vue
<template>
<div class="page-container">
<item
v-for="(category, index) in categories"
:key="index"
:item="category"
:is-selected="selectedItem === index"
#click="selectItem(index)"
/>
</div>
</template>
<script>
import { computed, ref } from 'vue'
import { useStore } from 'vuex'
import Item from '#/components/Item.vue'
export default {
components: {
Item
},
setup () {
const store = useStore()
store.dispatch('categories/getCategories')
const categories = computed(() => store.getters['categories/getCategories'])
const selectedItem = ref(1)
const selectItem = (index) => {
selectedItem.value = index
}
console.log(categories.value[selectedItem.value].id)
return {
categories,
selectedItem,
selectItem
}
}
}
</script>
<style lang="scss" scoped>
#import '#/assets/scss/general.scss';
</style>
categories.js - vuex module
import axios from 'axios'
import { API_URL } from '#/helpers/helpers'
export const categories = {
namespaced: true,
state: {
categories: []
},
getters: {
getCategories: (state) => state.categories
},
mutations: {
UPDATE_CATEGORIES: (state, newValue) => { state.categories = newValue }
},
actions: {
async getCategories ({ commit }) {
await axios.get(`${API_URL}/getCategories.php`).then(response => {
commit('UPDATE_CATEGORIES', response.data.res_data.categories)
})
}
},
modules: {
}
}
In the setup function you cannot process a computed function.
You can instead access store.getters['categories/getCategories'].value[selectedItem.value].id if you want to process that in the setup function.
When we use vue2 to create API, we just follow options API like below:
data are in data
methods are in methods
<script>
export default {
name: 'demo',
components: {},
filter:{},
mixins:{},
props: {},
data(){
return{
}
},
computed:{},
watch:{},
methods: {},
}
</script>
But the vue3 changed, how should I build a component with vue3 composition API?
Some example say that I should import reactive etc. From vue first and put all codes in setup(){}?
Some example show that I can add setup to <script>?
Please give me an example.
ok bro , Composition Api works like that:
<script>
import { fetchTodoRepo } from '#/api/repos'
import {ref,onMounted} from 'vue'
export default {
setup(props){
const arr = ref([]) // Reactive Reference `arr`
const getTodoRepo = async () => {
arr.value = await fetchTodoRepo(props.todo)
}
onMounted(getUserRepo) // on `mounted` call `getUserRepo`
return{
arr,
getTodoRepo
}
}
}
</script>
There are two ways to create a component in vue3.
One:<script> + setup(){},such as this:
<script>
import { reactive, onMounted, computed } from 'vue'
export default {
props: {
title: String
},
setup (props, { emit }) {
const state = reactive({
username: '',
password: '',
lowerCaseUsername: computed(() => state.username.toLowerCase())
})
onMounted(() => {
console.log('title: ' + props.title)
})
const login = () => {
emit('login', {
username: state.username,
password: state.password
})
}
return {
login,
state
}
}
}
</script>
Two:use <script setup="props">
loading....