Nuxt3 composition API dynamic $refs - vue.js

i've got an image loader component, which is getting the image id as prop, normally, i should use the ref like this:
<template>
<img
ref="foo"
/>
</template>
<script setup>
const foo = ref(null);
onMounted( () => {
const img = (foo.value as HTMLImageElement);
if (img) {
img.addEventListener("load", () => fitImage(img));
}
});
</script>
But how do i do it with dynamic ref?
<template>
<img
:ref="dynamicRef()"
/>
</template>
<script setup>
const dynamicRef = () => {
return 'image'+props.imageId;
}
</script>
I've already tried to place this inside an array, like
const refs = [];
refs[dynamicRef] = ref(null)
also
const refs = ref([])
refs.value[dynamicRef] = ref(null);
But nothing seems to work

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

How to make vue3 import async dynamic component work?

I am a beginner using vue3.
We can use dynamic component like this:
<script setup>
import CommonLayout from "#/components/Layout/CommonLayout.vue";
</script>
<template>
<component :is="CommonLayout>
</component >
</template>
and I try to use dynamic component like this,but it is wrong:
export default {
CommonLayout: () => import("./CommonLayout.vue"),
EmptyLayout: () => import("./EmptyLayout.vue"),
HeaderLayout: () => import("./HeaderLayout.vue"),
};
<script setup>
import layouts from "#/components/Layout/index.js";
const { default: Layout } = await layouts["CommonLayout"]();
</script>
<template>
<Layout>
something
</Layout>
</template>
not error catch but the page show nothing.
and the Layout is the same with CommonLayout:
You need to use defineAsyncComponent
<script setup>
import { defineAsyncComponent } from 'vue'
const CommonLayout = defineAsyncComponent(() => import("./CommonLayout.vue"))
const EmptyLayout = defineAsyncComponent(() => import("./EmptyLayout.vue"))
const HeaderLayout = defineAsyncComponent(() => import("./HeaderLayout.vue"))
</script>
<template>
<component :is="CommonLayout></component>
<component :is="EmptyLayout></component>
<component :is="HeaderLayout></component>
</template>

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>

Why does the reactivity of VUE 3 CompositionAPI not work?

Please tell me why reactivity between unrelated components does not work:
ModalsController.js:
import { ref } from 'vue';
export const useModal = (init = false)=>{
const isShowModal = ref(init);
const openModal = () => {
isShowModal.value = true;
};
const closeModal = () => {
isShowModal.value = false;
};
return {
isShowModal, openModal, closeModal
}
}
Header.vue:
<template>
<button #click="openModal">OpenModal</button>
{{isShowModal}}
<button #click="closeModal">CloseModal</button>
</template>
<script setup>
import {useModal} from "./ModalsController.js";
const { isShowModal,openModal,closeModal } = useModal();
</script>
Modal.vue:
<template>
<div v-if="isShowModal"> Modal window </div>
</template>
<script setup>
import {useModal} from "./ModalsController.js";
const {isShowModal} = useModal();
</script>
And everything works if I create a simple variable instead of a function like this:
ModalsController.js:
import { ref } from 'vue';
export const isShowModal = ref(false);
and accordingly, I change it in the header. But this is very inconvenient because there are way more functions (switching, etc.)
Thank you all in advance for your help. I put the code in the Playground for the test:
Not a working (func)
working (simple var)
The problem is useModal() creates a new ref() every time it's called. Each of your components calls useModal() to get the isShowModal ref, but each ref is a newly created one independent from each other.
To share the refs between components, move the ref creation outside of the useModal function definition:
import { ref } from 'vue';
const isShowModal = ref(false); 👈
export const useModal = (init = false) => {
// const isShowModal = ref(init); ❌ move this outside function
⋮
}
demo

Vue 3 props property not updating

I have parent component like below:
<template>
<button #click="initStr" value="init str" />
<child :str="str" />
</template>
<script>
export default {
components: { child, },
setup() {
const str= ref("");
function initStr() {
str.value = "init";
}
return {
str,
initStr,
};
}
};
</script>
The problem is when click the button on parent to init string, the child component does not rerender with the new string. I have to create another ref varible in child component then watch the props to assign the new string like below:
const string = ref(props.str);
watch(props, props => {
string.value = props.str;
});
Is this the only way to rerender the child when props from parent changes?
It should work as shown in the following example :
const {
createApp
} = Vue;
const App = {
setup() {
const str = Vue.ref("");
function initStr() {
str.value = "init";
}
return {
str,
initStr,
};
}
}
const app = createApp(App)
app.component('child', {
props: ['str'],
template: `
<div> str : {{str}}</div>
`
})
app.mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app">
<button #click="initStr">
init str
</button>
<child :str="str" />
</div>