How to reference modelValue from a function in a VUE componenet - vue.js

I am using Vite to build my Vue SFCs. I need to access the modelValue inside a function declared by the component. How do I do it?
Here is what I tried:
<script setup>
defineProps([ 'modelValue' ]);
defineEmits([ 'udpate:model-value' ]);
function portPlaceholder() {
// ERROR THROWN HERE, modelValue is not defined
if ( modelValue?.type === 'example' ) return 'something';
if ( modelValue?.type === 'other' ) return 'something-else';
return 'default-something';
}
</script>
<template>
Some input: <input type="text" :placeholder="portPlaceholder()" />
</template>

Try to assign the define props to a prop constant the use that constant to access the prop field, and use computed property instead of the function :
<script setup>
import {computed} from 'vue';
const props = defineProps([ 'modelValue' ]);
const emit = defineEmits([ 'udpate:model-value' ]);
const portPlaceholder = computed(()=>{
if ( props.modelValue?.type === 'example' ) return 'something';
if ( props.modelValue?.type === 'other' ) return 'something-else';
return 'default-something';
})
</script>
<template>
Some input: <input type="text" :placeholder="portPlaceholder" />
</template>

Related

Get data from a vue component child and bind to an object in parent component

<user-data #change="setUserInfo"></user-data">
this is the child component where have used emits to pass data.
here is the method of parent component.
setUserInfo(data) {
this.obj.payment_details = data;
},
is it possible to bind data from the above method?
export default {
data: () => ({
dialog: false,
obj: new Expense(),
saveLoader: false,
}),
}
Here you have an example on how to emit data from child component to parent (using Vue3 Composition API script setup):
Parent:
<template>
<Comp #my-var="callback" />
{{ test }}
</template>
<script setup>
import { ref } from 'vue'
import Comp from './Comp.vue'
const test = ref('')
const callback = data => test.value = data
</script>
Child:
<template>
<button
v-text="'click'"
#click="doEmit()"
/>
</template>
<script setup>
const emits = defineEmits(['myVar'])
const doEmit = () => emits('myVar', 'emiting this data')
</script>
Check out the Playground

Vue 3 composition api - computed property is returning undefined

Using the Vue 3 composition API, how can I return the computed value for the property, firstDigit? The keyword, this, in the computed property is undefined but when I leave this out, then I get the error fourDigits is not defined.
<script setup>
import { computed, reactive } from 'vue'
const input = reactive({
fourDigits: Array(1,2,3,4),
firstDigit: computed(() => {
return this.fourDigits[0] <===== `this` is undefined but if I leave `this` out, then `fourDigits` is undefined.
})
</script>
<template>
<div>
<pre>
{{JSON.stringify(input.firstDigit, null, 2)}}
</pre>
</div>
</template>
this is something else in composition API , try with:
firstDigit: computed(() => {
return input.fourDigits[0]
})
If I need to use state property to assign a value to another state property, I do it in onMounted() hook. Like this:
<script setup>
import { computed, reactive } from 'vue'
const input = reactive({
fourDigits: Array(1, 2, 3, 4),
firstDigit: computed(() => {
return 0; // just some default value
})
});
onMounted(() => {
input.firstDigit = input.fourDigits[0];
})
</script>
<template>
<div>
<pre>
{{ JSON.stringify(input.firstDigit, null, 2) }}
</pre>
</div>
</template>
Check if it will work for you. All the best!

vue3 some variable is set by props or ref

// pseudo code
// same type between modelValue and image
props = { modelValue? }
image = ref()
onPageLoad = function {
if modelValue is null then:
image.value = await api.get()
}
<button #onClick>
// if model is null
<image src="image?.src" />
// else
<image src="modelValue?.src" />
how to do that?
modelValue props set optionally.
if not set, then ref set with onPageLoad
I rendering by modelValue or ref
should I use watch? or watchEffect?
Define the modelValue as prop and update the image ref in onMounted hook, then use a computed property to return the image source :
<script setup>
import {ref, computed, onMounted} from 'vue'
const props = defineProps(['modelValue']);
const image=ref('')
onMounted(async ()=>{
image.value = await api.get()
})
const src = computed(()=>props.modelValue.src??image.value)
</script>
<img :src="src" />

Extract modelValue logic to composable

I'm transitioning from Vue 2 to Vue 3 and I'm having trouble with composables.
I have a bunch of components that inherits modelValue. So, for every component that uses modelValue I'm writing this code (example with a radio input component):
<script setup>
import { computed } from 'vue'
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
modelValue: {
type: [String, null],
required: true
}
})
const computedValue = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
</script>
<template>
<label class="radio">
<input
v-model="computedValue"
v-bind="$attrs"
type="radio"
>
<slot />
</label>
</template>
Is there a way to reuse the code for the modelValue?
I've just done this while I'm playing with Nuxt v3.
You can create a composable like this:
import { computed } from 'vue'
export function useModel(props, emit) {
return computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
}
<template>
<input type="text" v-model="value" />
</template>
<script setup lang="ts">
const props = defineProps({
modelValue: String,
})
const emit = defineEmits(['update:modelValue'])
const value = useModel(props, emit)
</script>
For completion of #BghinC's perfect answer here the fully typed version:
Composable
File: #/composables/useModelValue.ts
import {computed} from 'vue'
export default function useModelValue<T>(
props: {
modelValue: T
[key: string]: unknown
},
emit: (event: 'update:modelValue', ...args: unknown[]) => void
) {
return computed({
get: () => props.modelValue,
set: (value: T) => emit('update:modelValue', value),
})
}
Usage
<script setup lang="ts">
import useModelValue from '#/composables/useModelValue'
const props = defineProps<{
modelValue: Dog
}>()
const emit = defineEmits(['update:modelValue'])
const dog = useModelValue<Dog>(props, emit)
</script>

How to use v-model in Vue with Vuex and composition API?

<template>
<div>
<h1>{{ counter }}</h1>
<input type="text" v-model="counter" />
</div>
</template>
<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup() {
const store = useStore()
const counter = computed(() => store.state.counter)
return { counter }
},
}
</script>
How to change value of counter in the store when input value changes
I am getting this error in the console:
[ operation failed: computed value is readonly ]
Try this:
...
const counter = computed({
get: () => store.state.counter,
set: (val) => store.commit('COUNTER_MUTATION', val)
})
...
https://v3.vuejs.org/api/computed-watch-api.html#computed
Try this:
<input v-model="counter">
computed: {
counter: {
get () {
return this.$store.state.a
},
set (value) {
this.$store.commit('updateA', value)
}
}
}
With composition API
When creating computed properties we can have two types, the readonly and a writable one.
To allow v-model to update a variable value we need a writable computed ref.
Example of a readonly computed ref:
const
n = ref(0),
count = computed(() => n.value);
console.log(count.value) // 0
count.value = 2 // error
Example of a writable computed ref:
const n = ref(0)
const count = computed({
get: () => n.value,
set: (val) => n.value = val
})
count.value = 2
console.log(count.value) // 2
So.. in summary, to use v-model with Vuex we need to use a writable computed ref. With composition API it would look like below:
note: I changed the counter variable with about so that the code makes more sense
<script setup>
import {computed} from 'vue'
import {useStore} from 'vuex'
const store = useStore()
const about = computed({
get: () => store.state.about,
set: (text) => store.dispatch('setAbout', text)
})
</script>
<template>
<div>
<h1>{{ about }}</h1>
<input type="text" v-model="about" />
</div>
</template>