I need to make a composable universal.
Here’s my case, I have 3 components – ItemSwiper
, ItemCard
and ViewRecipeDetail
.
ItemSwiper
contains card slides and it is the parent of ItemCard
.
ItemSwiper
has a loop of recipes:
<Slide v-for="recipe in storeRecipe.data" :key="recipe.recipe_id">
<ItemCard :data="recipe"/>
</Slide>
Here I’m passing the data prop to ItemCard
.
Then, in the ItemCard
I use this prop to display the information:
<template>
// here I'll add a skeleton loader that will be shown when the image is loading.
<img class="card__image" :src="getSrc('.jpg')" :alt="data.alt" />
</template>
<script setup>
const props = defineProps(['data', 'pending']);
const isLoaded = ref(false);
const getSrc = ext => {
return new URL(
`../assets/images/content/recipe/${props.data.image}${ext}`,
import.meta.url
).href;
};
onMounted(() => {
const img = new Image(getSrc('.jpg'));
img.onload = () => {
isLoaded.value = true;
};
img.src = getSrc('.jpg');
});
</script>
I need to use this getSrc
function and image preload in the onMounted
hook in another component – ViewRecipeDetail
, which isn’t related to these two. In ViewRecipeDetail
will be displayed the detailed information about a recipe.
I was thinking of moving this function and a hook into composable useRecipe
and then use this composable in ItemCard
and in ViewRecipeDetail
.
However, due to the fact that in ItemSwiper
I pass the data
prop, which has recipe
as its value, i.e. recipe in a loop, if I pass this prop as a parameter like this:
import { useRecipe } from '@/composable/useRecipe';
const props = defineProps(['data', 'pending']);
const { isLoaded, getSrc } = useRecipe(props.data);
Then in useRecipe
we can use it like this:
import { onMounted, ref } from 'vue';
export function useRecipe(data) {
const isLoaded = ref(false);
const getSrc = ext => {
return new URL(
`../assets/images/content/recipe/${data.image}${ext}`,
import.meta.url
).href;
};
onMounted(() => {
const img = new Image(getSrc('.jpg'));
img.onload = () => {
isLoaded.value = true;
};
img.src = getSrc('.jpg');
});
return {
isLoaded,
getSrc,
};
}
This will work for ItemCard
, but it won’t work for ViewRecipeDetail
. Because I don’t need any loop in ViewRecipeDetail
. All I need to do is go to that recipe detail page and see the relevant information for that particular recipe.
It turns out that useRecipe
is not universal now. We pass props.data
as a parameter and it works for ItemCard
, but it won’t work for ViewRecipeDetail
, because we need storeRecipe.data
instead of props.data
.
And here’s ViewRecipeDatail
. Please, tell me if I’m doing something wrong. I want to display an image same as I did in ItemCard
, using a composable, but without a prop:
<template>
<img
class="card__image"
:src="getSrc('.jpg')"
:alt="storeRecipe.data.alt" />
<div class="card__content">
<h2 class="card__title">
{{ storeRecipe.data.title }}
</h2>
<p class="card__text">
{{ storeRecipe.data.short_description }}
</p>
</div>
</template>
<script setup>
import { useStoreRecipe } from '@/store/storeRecipe';
import { useRecipe } from '@/composable/useRecipe';
const storeRecipe = useStoreRecipe();
const { getSrc } = useRecipe(storeRecipe.data);
</script>
Please, give me a possible solution. (If you don’t understand something, please, let me know).
2
Answers
It’s really hard to understand your question ,you just say won’t work,but how it’s won’t work,what’s the error message,if you had debug the code? can you show code on codepen that i can run it
I spent too much time on this :p but I wanted to see if composable was possible for what you want to do
There are a few changes to the composable, it returns a
src
andisLoaded
(though, that’s a bit redundant assrc
isnull
andisLoaded
isfalse
until the image loads, at which pointsrc
has the href, andisLoaded
becomes true – you can see the redunancy there.Anyway
The composable,
useRecipe.js
ItemCard.vue
– changed for the alternative I proposeItemSwiper.vue
– unchangedViewRecipeDetail.vue
– I’m assumingstoreRecipe.data
here is NOT an Array, and not the same asstoreRecipe.data
inItemSwiper.vue
, since you don’t treat it like an array in your code! Also, your description suggests this is a single recipe. Not sure why the variable name is the same as in ItemSwiper though.