What is the best way to transfer data to a component that is not directly related

I am looking to showcase an image, title, and description for a specific recipe that I select.

Here is my setup using ItemSwiper (parent):

<template>
     <Slide
         v-for="recipe in storeRecipe.data"
         :key="recipe.recipe_id">
         <ItemCard :data="recipe" :pending="storeRecipe.pending" />
     </Slide>
</template>

<script setup>
import { onMounted } from 'vue';
import { useStoreRecipe } from '@/store/storeRecipe';
import ItemCard from '@/component/ItemCard.vue';

// Pinia store
const storeRecipe = useStoreRecipe();

onMounted(async () => {
    await storeRecipe.loadRecipes();
});
</script>

Now in the child component, ItemCard:

<template>
    <div class="card">
        <div class="card__item">
            <img
                class="card__image"
                :src="getSrc('.jpg')"
                :alt="data.alt"/>
            <div class="card__content">
                <h2 class="card__title">{{ data.title }}</h2>
                <p class="card__text">{{ data.short_description }}</p>
                <router-link
                    class="card__link"
                    :to="{
                        name: 'recipeDetail',
                        params: { recipe: data.slug },
                    }"
                    >View more</router-link
                >
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

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>

When clicking on the 'View More' link, it leads to the recipe detail page (ViewRecipeDetail):

<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';
const storeRecipe = useStoreRecipe();
</script>

I'm trying to figure out how to show the same content (image, title, etc.) in ViewRecipeDetail as in ItemCard, even though they are not directly related. Additionally, I want to access the getSrc function in ViewRecipeDetail without duplicating code. Implementing this using a composable has proven challenging.

UPDATE: I have added a store action and state:

Action:

export const actions = {
    async loadRecipes(keyword) {
        try {
            this.pending = true;
            keyword ??= '';
            const res = await fetch(
                `/api/recipe?keyword=${encodeURIComponent(keyword)}`
            );
            if (res.ok) {
                const data = await res.json();
                this.data = data;
            }
        } catch (err) {
            console.error('Error fetching data:', err);
            this.error = 'Failed to fetch data';
        } finally {
            this.pending = false;
        }
    },
};

State:

export const state = () => {
    return {
        data: [],
        pending: false,
    };
};

Please provide a possible solution. If there's a way to utilize the store without query parameters in the router-link, that would be ideal. Thank you!

Answer №1

Sure thing, it's definitely possible. If you're already utilizing Pinia, you can create a shared state that both 'ItemCard' and 'ViewRecipeDetail' can access.

Start by creating an action to set the selectedRecipe data:

// storeRecipe.js

export const state = () => ({
    data: [],
    pending: false,
    selectedRecipe: null,
});

export const actions = {
    async loadRecipes(keyword) {
        try {
            this.pending = true;
            keyword ??= '';
            const res = await fetch(
                `/api/recipe?keyword=${encodeURIComponent(keyword)}`
            );
            if (res.ok) {
                const data = await res.json();
                this.data = data;
            }
        } catch (err) {
            console.error('Error fetching data:', err);
            this.error = 'Failed to fetch data';
        } finally {
            this.pending = false;
        }
    },

    selectRecipe(recipe) {
        this.selectedRecipe = recipe;
    },
};

Next, dispatch the action to set the selected recipe when the router-link is clicked:

// ItemCard.vue

<router-link
    class="card__link"
    :to="{ name: 'recipeDetail' }"
    @click="handleClick"
>
    View more
</router-link>

<script setup>
import { useStoreRecipe } from '@/store/storeRecipe';
const storeRecipe = useStoreRecipe();

const handleClick = () => {
    storeRecipe.selectRecipe(props.data);
};
</script>

Finally, retrieve the selected recipe from the store:

// ViewRecipeDetail.vue

<template>
    <div v-if="storeRecipe.selectedRecipe">
        <img
            class="card__image"
            :src="getSrc('.jpg')"
            :alt="storeRecipe.selectedRecipe.alt"
        />

        <div class="card__content">
            <h2 class="card__title">
                {{ storeRecipe.selectedRecipe.title }}
            </h2>
            <p class="card__text">
                {{ storeRecipe.selectedRecipe.short_description }}
            </p>
        </div>
    </div>
</template>

<script setup>
import { useStoreRecipe } from '@/store/storeRecipe';
const storeRecipe = useStoreRecipe();
</script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Prevent data loss on webpage refresh by using Angular's local storage feature

As a beginner in Angular, I am exploring ways to retain user input and interactions on my webpage even after a refresh. After some research, I came across using local storage as a viable solution. A different answer suggested utilizing the following code s ...

Guide on using JavaScript to extract and display a random text from a datalist upon clicking with the onclick event

Is there a way for a JavaScript function to select a random sentence from a datalist within the same file and display it upon clicking a button with an "onclick" event? I'm new to JavaScript and seeking the simplest solution. Can anyone provide an exa ...

Bottom-aligning text in Tailwind CSS

Creating two <p> tags to store text: <p v-if="my_property <= 0" class="text-green-500 text-lg font-bold">{{Math.abs(my_value)}}%</p> <p v-else class="text-red-500 text-lg font-bold">{{my_value}}%< ...

What is the best way to divide files in a Vue.js-based spa into both public and private sections?

Looking to divide my Vue.js and Ionic SPA app into a public section (featuring just login, password request, and minimal content) and the remaining functionality... I've come across the possibility of implementing a Multi-Page Application utilizing w ...

Does Node.js offer a solution or module for converting Google Map polylines into a PNG image without the need for rendering HTML?

I'm attempting to generate a PNG image of polylines on a Google Map using Node.js on the backend without utilizing any view engine. I attempted to use the "webshot" Node.js package, but it was unable to save the polylines on the Google Map. app.get( ...

Easily use Discord.JS 13 slash commands to generate an embed displaying a user's specific role within a server

Currently, I am in the process of creating a slash command that will reveal a user's specific roles when the command is triggered. run: async (client, interaction) => { try{ const { member, channelId, guildId, applicationId, comman ...

assisting with the transition effect using CSS3, JS, or jQuery

I am looking to alter the background-image of this particular image when transitioning to another one from my images folder, similar to this example. Below is the code that I have: CSS .image { position: relative; overflow: hidden; -webkit-tr ...

Tips for accessing the URL in PhoneGap and parsing the response with jQuery

Currently, I am working on a task in PhoneGap which involves creating a registration page. When the user clicks on the registration page, the values entered will be sent to a specific URL. If the user is already registered or not, the data will be returned ...

What specific portion of the code will be transformed into a virtual DOM?

As a newcomer to the virtual DOM concept, I have been pondering how it functions over the past few days. Let's envision that I have integrated a simple template engine like twig into my project, and I am utilizing vue.js as my chosen javascript frame ...

Can phantomJS be used to interact with elements in protractor by clicking on them?

While attempting to click a button using PhantomJS as my browser of choice, I encountered numerous errors. On my first try, simply clicking the button: var button = $('#protractorTest'); button.click(); This resulted in the error: Element is ...

Tips for adjusting the color of the white tick in Material UI Checkbox with React and JavaScript

I'm currently attempting to customize the default white tick (not the checkbox background!) and change it to a different color that I choose. While I have come across solutions for earlier versions of MUI and React, I am specifically looking for a sol ...

Unleashing the power of storytelling with React: A guide to creating dynamic story

weather.stories.ts export default { title: 'Widgets/Forecast', component: Weather, } const Template: Story<any> = (args) => <Weather {...args} />; export const Default = Template.bind({}); Default.args = { forecast: { ...

Encountering the "excessive re-renders" issue when transferring data through React Context

React Context i18n Implementation i18n .use(initReactI18next) // passes i18n down to react-i18next .init({ resources: { en: { translation: translationsEn }, bn: { translation: translationsBn }, }, lng: "bn ...

Update the document by sending the data and making changes based on the AJAX response

Currently, I am avoiding using jQuery and trying to work with the following code in PHP: <?php header ( 'Content-Type: text/xml; charset=utf-8' ); $con = @mysql_connect ( "localhost", "root", "" ) or die ( "Couldn't connect to database" ...

The Chrome application does not retain cookies

Why is the cookie url_user not being stored? Below is the content of index.html: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial- ...

Direct attention to the modal display

I'm having trouble auto-focusing an input field when a modal is displayed. Here's what I've tried, but it doesn't seem to be working: jQuery(document).ready(function($) { $('#myModal').on('show.bs.modal', functi ...

Tips for transforming a date into a time ago representation

Can someone help me with converting a date field into a "timeago" format using jquery.timeago.js? $("time.timeago").timeago(); var userSpan = document.createElement("span"); userSpan.setAttribute("class", "text-muted"); userSpan.appendChild(document.crea ...

Implementing a function in jQuery to create a "Check All" and "Uncheck All" button

Can someone please guide me on how to implement a check all and uncheck all functionality when I check individual checkboxes one by one? Once all checkboxes are checked, the 'checkall' checkbox should automatically be checked. Below is the code s ...

Improving a Vue.js notification component with data retrieved from a fetch request result

Struggling with updating the content inside a vuetify v-alert. There's an issue when trying to update Vue component properties within the sessionID.then() block after logging into a system and receiving a session id. Vue.component('query-status ...

Mastering asynchronous props handling with Vue 3's composition API

Starting Component: const { receiveData, deletePost, erasePhonebook, fetchCount, issue } = useSections(); const section = ref({}); receiveData(section_id).then((s) => { section.value = s; }); Sub Component: const { section } = defineProps({ secti ...