Detect Updates in Nested Vue.js Properties and Execute Functions Within a Complex Component Hierarchy

Working on a Vue.js project, I find myself needing to monitor changes in a nested property (isChecked) within a complex component structure. My aim is to execute a method (sumOfChecked) whenever the isChecked property changes in any row or its child rows.

Below is a simplified representation of my component hierarchy:

<template>
    <div class="tw-max-w-max">
        <div v-for="(row, index) in rows" :key="index">
            <fieldset class="tw-grid tw-grid-cols-[2fr_100px_120px_100px_100px_30px] tw-gap-4">
                <v-list-item-title class="tw-text-sm tw-flex tw-justify-between tw-items-center">
                    <span>{{ row.kostenstelleName }}</span>
                    <v-checkbox
                        v-model="row.isChecked"
                        @change="onCheckboxClicked"
                    />
                </v-list-item-title>
                <!-- Other fields omitted for brevity -->
            </fieldset>
            <KreuztabelleRow
                v-if="row.subRows.length"
                :is-subitem="true"
                :kreuztabelle="{rows: row.subRows}"
            />
        </div>
    </div>
</template>

<script lang="ts">
import { Component, Emit, Prop, Vue, Watch } from 'vue-property-decorator';
import KreuztabelleRow from './KreuztabelleRow.vue';

@Component({
    components: { KreuztabelleRow },
})
export default class KreuztabelleRow extends Vue {
    @Prop({ required: true, type: Object }) kreuztabelle;
    @Prop({ required: false, default: false }) isSubitem;

    get rows() {
        return this.kreuztabelle?.rows || [];
    }

    @Emit('sum-of-checked')
    onCheckboxClicked() {
        return this.sumOfChecked();
    }

    sumOfChecked() {
        const calculateSum = (rows) => {
            return rows.reduce((sum, row) => {
                if (row.isChecked) {
                    sum += row.summe || 0;
                }
                if (row.subRows && row.subRows.length > 0) {
                    sum += calculateSum(row.subRows);
                }
                return sum;
            }, 0);
        };

        return calculateSum(this.rows);
    }

    @Watch('rows', { immediate: true, deep: true })
    onRowsChange(newRows) {
        newRows.forEach((row) => {
            this.$watch(() => row.isChecked, this.onCheckboxClicked, { deep: true, immediate: true });
            row.subRows?.forEach((subRow) => {
                this.$watch(() => subRow.isChecked, this.onCheckboxClicked, { deep: true, immediate: true });
            });
        });
    }
}
</script>

The Issue: Despite my efforts, the sumOfChecked method does not trigger when the isChecked property changes. It should be called whenever a checkbox is toggled, even within nested sub-rows.

My Attempts: I set up a deep watcher on rows to detect changes in isChecked properties. Integrated a recursive function inside sumOfChecked to compute the total of checked rows, including sub-rows. Ensured that the onCheckboxClicked method is triggered upon checkbox change. Unfortunately, the sumOfChecked method fails to activate as anticipated.

Query: How can I effectively monitor changes in the isChecked property of each row and its sub-rows to trigger the sumOfChecked method accordingly? Is there a more efficient approach to achieve this in Vue.js?

Answer №1

The solution lies within the problem definition itself. Changes are easily traced in the specific location where they occur, such as the v-checkbox. When encountering difficulties tracking these changes later on with a watcher, using it becomes the most viable option.

It is not recommended to combine v-model with events, as this can lead to unpredictable behavior that has not been documented. Similarly, utilizing the change event on a component without proper documentation is a mistake. If additional logic needs to be applied to v-model, it should be deconstructed like so:

<v-checkbox
    :modelValue="row.isChecked"
    @update:modelValue="onCheckboxClicked(row, $event)"
/>

With the event listener accepting a value and enabling various actions to be performed:

onCheckboxClicked(row, value) {
  row.isChecked = value;
  this.sumOfChecked();
}

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

What is a functional component in defineSlots?

With the latest version of Vue SFC (3.3+), slot scope has become type safe with the introduction of defineSlots. Is there a way to achieve the same type safety for slots in functional components as well? export default defineComponent({ name: 'MyCompo ...

Having trouble locating the data-testid attribute within a Vue Component using Jest

I need assistance in constructing a test that specifically selects an element utilizing the data-testid attribute. The scenario involves a BaseTile component structured as follows: <template> <div data-testid="base-tile-icon&quo ...

Having trouble implementing the Material UI time picker because it does not meet the required DateTime format

REVISE I recently switched my dataType from DateTime to TimeSpan in my code. I have a functioning MVC version that already uses TimeSpan, and the times are posted in HH:MM format. Now, I am unsure if the issue lies with the headers set up on Axios or if it ...

Vue-i18n: displaying views based on language

Currently, I am utilizing vue-i18n in my application to support multiple languages. My next task involves creating an "About" view that will contain a substantial amount of text and various links. Instead of cluttering the about.vue file with numerous {{ ...

How can I switch the header bar button from "login" to "logout" in a React

I am currently working on a React and Next.js frontend template, specifically focusing on creating a dashboard with a header bar and login button. Once a user logs in, the login button should change to a logout button and the links in the header should als ...

Is there a way to detect when a Vue popover trigger occurs across multiple pages within a project?

Throughout my entire project, I am utilizing v-b-popover extensively across multiple pages. I am in search of a method to determine when a trigger occurs whenever the popover button is clicked or hovered over on any given page. I am looking for a solution ...

I am facing hurdles with firebase permissions as it is not granting me the necessary access

https://i.sstatic.net/oYAdY.png firebase.database().ref('meetups').push(meetup) .then((data)=> { console.log(data) commit('createMeetup', meetup) }) .catch((error ...

Transform JSON object to a class/interface object using Typescript

I am currently working on converting an API response into a TypeScript class or interface. The API is returning a list of objects with various properties, but I only require a few specific properties from the response object. Example of API Response: ...

What is the best way to send props to a component's story?

Struggling with incorporating stories into the vue-select component using Storybook, especially in more complex cases involving passing props or methods. When attempting to pass props within the template it functions correctly: storiesOf('VSelect&ap ...

The underscore convention for defining members in Typescript allows for clear and concise

Let's talk about a class called Email class Email { private _from: string; private _to: Array<string>; private _subject: string; } When an email object is created, it will look something like this: { _from:'', _to:'&apo ...

Automatically Refreshing on Vue.js: Embracing the Passage of Time

I want to display this clock on Vue. The clock should automatically update the time, but how can I make this code work in Vue? Is there a simple way to implement this in Vue without using (document.getElementBy...) function updateTime () { document.ge ...

Capturing a webpage through Record RTC integration with Angular

I am looking to record a specific section of the page as a video with audio. For example, when a user enters the page, I want it to automatically start recording the activities in that particular area (similar to how iframe videos work). The recording sh ...

Is it possible to utilize v-if and v-for outside of the HTML element?

My task is to assign the entire list to a variable if the condition is met, otherwise I must iterate through the list and assign each element separately. <ul> <li v-if="flag = true"> <myVueComponent v-bind: ...

Vue Js does not include images in the dist directory when the build process is completed

In my VueJs 3 project, I am working with a list of PNG images stored in the src/assets/pngs/ directory. Within my Vue component, I use a For loop to dynamically create the list by setting the image name as the source for the img tag. This implementation wo ...

What is the rationale behind permitting surplus properties in Typescript interfaces even though all properties are declared as optional?

Exploring the code snippet... interface Options { allowed?: string; } function test(options: Options) { return options; } const options = { allowed: 'allowed', notAllowed: 'notAllowed', }; test(options); // no error thrown ...

What is the best way to clear the states of a Nuxt app when logging out?

After a user logs out of the app, the Pinia persistent authStore state is reset and they are redirected to /login. However, if another user logs in without refreshing the page, the main page may still display the previous user's data because it remain ...

Using VueJS, you can integrate the v-model directive with the v

Here is the specific requirement I have. I need to display a question paper using v-for loop with an object. When a user selects an answer for a question, the question number (index) should be bound to that answer using v-model. How can I accomplish this ...

Typescript interface designed for objects containing certain optional property names as well as unspecified property names

I am looking to design an interface for an object that can have optional properties with specific names, as well as accept properties with arbitrary names. Here is my approach: interface CallBack { onTransition?(): any; // option A [key: string]: () = ...

Error: The nested property of the dynamic type cannot be indexed

Within my coding project, I've devised an interface that includes various dynamic keys for API routes, along with the corresponding method and response structure. interface ApiRoutes { "auth/login": { POST: { response: { ...

What is the best way to execute TypeScript programs on an Android device?

Is there a way to execute TypeScript programs on an Android phone? Are there any offline apps specifically designed for running TypeScript programs on Android devices? ...