Search code examples
vue.jsvuejs2vuejs3vue-componentvuetify.js

Dynamically fetch data in parent component when new data is added in child component in Vue without using watcher


Is there any way in Vue to display the data when it is updated other than using watcher ? Just like we have useEffect in React which renders when the data is changed, is there anyway we can do something similar without using Watcher ? Because watcher is taking dedicated resources for continuously watching over the state.

I am want to display the name of the user on the left side as soon as user enter the same in some other page.

Alternative of Watcher in Vue

Here's the code

    <template>
    <div>
        <q-layout view="hHh lpR lFf">
            <!-- Header -->
            <q-header elevated :class="$q.dark.isActive ? 'bg-secondary' : 'bg-black'">
                <q-toolbar>
                    <q-btn flat @click="drawer = !drawer" round dense icon="menu"></q-btn>
                    <q-toolbar-title>Data Templates</q-toolbar-title>
                </q-toolbar>
            </q-header>

            <!-- Page Content -->
            <q-page-container>
                <router-view></router-view>
                <CreateTemplate v-if="showCreateTemplate" />
                <EditTemplate v-if="showEditTemplate" :id="template._id" />
                <DeleteTemplate v-if="showEditTemplate" :id="template._id" />
                <SpecificTemplate v-if="showEditTemplate" :id="template._id" />
            </q-page-container>

            <!-- Sidebar -->
            <q-drawer v-model="drawer" show-if-above class="drawer">
                <q-scroll-area class="fit">
                    <div class="sidebar">
                        <div class="sidebar-content">
                            <q-btn class="data-button" color="primary" label="Create New Template" dense
                                @click="navigateTo()" />
                            <div class="template-list">
                                <q-item v-for="(templateItem) in template" :key="templateItem._id">
                                    <q-item-section>
                                        <div class="template-item">
                                            <div @click="gotoSpecificTemplate(templateItem._id)">{{ templateItem.name ?
                                                templateItem.name : "No Name" }}</div>
                                            <div class="template-buttons">
                                                <q-btn color="red" dense
                                                    @click="navigateDeleteTo(templateItem._id)">
                                                    <font-awesome-icon icon="trash" class="template-icon" />
                                                </q-btn>
                                                <q-btn color="primary" dense
                                                    @click="navigateEditTo(templateItem._id)">
                                                    <font-awesome-icon icon="pen" class="template-icon" />
                                                </q-btn>
                                            </div>
                                        </div>
                                    </q-item-section>
                                </q-item>
                            </div>
                        </div>
                    </div>
                </q-scroll-area>
            </q-drawer>
        </q-layout>
    </div>
</template>
<script>
import CreateTemplate from '../components/MainLayout.vue';
import EditTemplate from '../components/EditLayout.vue';
import DeleteTemplate from '../components/DeleteLayout.vue';
import SpecificTemplate from '../components/SpecificLayout.vue';

import { defineComponent, ref, onMounted, watch } from 'vue';
import {
    QLayout,
    QDrawer,
    QScrollArea,
    QToolbarTitle,
    QItem,
    QBtn,
} from 'quasar';
import axios from 'axios';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { faTrash, faPen } from '@fortawesome/free-solid-svg-icons';
import { useRouter } from 'vue-router';

library.add(faTrash, faPen);

export default defineComponent({
    name: 'AppLayout',
    components: {
        QLayout,
        QDrawer,
        QScrollArea,
        QToolbarTitle,
        QItem,
        QBtn,
        CreateTemplate,
        EditTemplate,
        FontAwesomeIcon,
        DeleteTemplate,
        SpecificTemplate

    },
    setup() {
        const drawer = ref(false);
        const buttonData = ref([]);
        const text = ref('');
        const template = ref([]);
        const showCreateTemplate = ref(false);
        const showEditTemplate = ref(false);
        const showDeleteTemplate = ref(false);
        const showSpecificTemplate = ref(false);
        const router = useRouter();

        const fetchAllTemplates = async () => {
            try {
                const templateResponse = await axios.get('https://my-api.com/templates');
                const templates = templateResponse.data;
                template.value = templates;
            } catch (error) {
                console.error('Error fetching templates: ', error);
            }
        };

        onMounted(() => {
            fetchAllTemplates();
        });

        watch(template, () => {
            fetchAllTemplates();
        });

        const navigateTo = () => {
            router.push({ name: 'create-template' });
            showCreateTemplate.value = false;
            showEditTemplate.value = false;
        }

        const navigateEditTo = (templateId) => {
            router.push({ name: 'update-template', params: { id: templateId } });
            showEditTemplate.value = false;
        };

        const navigateDeleteTo = (templateId) => {
            router.push({name: 'delete-template', params: {id: templateId}});
            showDeleteTemplate.value = false;

        }
        const gotoSpecificTemplate = (templateId) => {
            try {
                router.push({ name: 'specific-template', params: { id: templateId } });
            } catch (error) {
                console.error('There is some error: ', error);
            }
        };

        return {
            drawer,
            buttonData,
            text,
            template,
            showCreateTemplate,
            showEditTemplate,
            showDeleteTemplate,
            showSpecificTemplate,
            navigateTo,
            navigateEditTo,
            gotoSpecificTemplate,
            navigateDeleteTo
        };
    },
});
</script>

Solution

  • I found the solution. What I did was I emitted a Boolean value from child component. That value is initially false. When a button is clicked in child component, it is converted into true and sent to the parent component. In parent component, I used method instead of setup, to fetch the data using fetch function ( I took it out of setup and placed it in methods only ) when the Boolean value received from the child component is true. I used async functions and await to make it smooth like when api display only when api is called and meanwhile wait for it.