Implementing reactivity in vue-chartjs using the Vue3 Composition API and data from an object

I have been struggling to update a Bar Graph dynamically whenever there is a change in the data object. Despite using reactive() and ref(), I am unable to achieve the desired functionality as the graph only updates upon page refresh.

Below is my chart component:

<script setup lang="ts">
import { Bar } from "vue-chartjs"
import {
    Chart as ChartJS,
    Title,
    Tooltip,
    Legend,
    BarElement,
    CategoryScale,
    LinearScale,
} from "chart.js"

ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)

const props = defineProps<{ userHours: {} }>()

const chartData = ref({
    labels: Object.keys(props.userHours),
    datasets: [
        {
            label: "Worker Hours per Employee",
            data: [...Object.values(props.userHours)] as number[],
            maxBarThickness: 32,
            backgroundColor: "#ffb53c",
        },
    ],
})

const chartOptions = ref({
    responsive: true,
    maintainAspectRatio: true,
    scales: {
        x: {
            grid: {
                color: "black",
            },
            ticks: {
                color: "#eee",
            },
        },
        y: {
            grid: {
                color: "black",
            },
            ticks: {
                color: "#eee",
            },
        },
    },
    legend: {
        labels: {
            fontColor: "#eee",
        },
    },
})

const chartStyles = computed(() => {
    return {
        width: "100%",
        height: "20ch",
    }
})
</script>

<template>
    <Bar
        id="workhr-chart"
        :options="chartOptions"
        :data="chartData"
        :style="chartStyles"
    >
    </Bar>
</template>

Here is the main page (parent) code snippet:

<script setup lang="ts">
import { User, Subtask } from ".prisma/client"
import { optionalMemberExpression } from "@babel/types"

const memberHours = reactive(initHours())

function initHours(): { [key: string]: number } {
    const hoursByMember: { [key: string]: number } = {}

    const tasks = project.value!.tasks.filter(
        task => task.status === 0 || task.status === 1,
    )
    for (const task of tasks) {
        for (const member of projectMembers) {
            if (task.assignees.includes(member)) {
                if (hoursByMember.hasOwnProperty(member.name)) {
                    hoursByMember[member.name] += task.workerHours
                } else {
                    hoursByMember[member.name] = task.workerHours
                }
            }
        }
    }
    return hoursByMember
}

function updateHours(uid: number, status: boolean) {
    let task = project.value!.tasks.find(task => task.uid === uid)
    let foundSubtask: Subtask | undefined

    if (!task) {
        // Check if the uid is a subtask
        for (const t of project.value!.tasks) {
            foundSubtask = t.subtasks.find(subtask => subtask.uid === uid)
            if (foundSubtask) {
                task = t
                break
            }
        }
    }
    if (!task) {
        return
    }

    const assignees = new Set(task.assignees.map(member => member.name))

    for (const memberName of Object.keys(memberHours)) {
        if (assignees.has(memberName)) {
            if (status) {
                memberHours[memberName] -= foundSubtask
                    ? foundSubtask.workerHours
                    : task.workerHours
            } else {
                memberHours[memberName] += foundSubtask
                    ? foundSubtask.workerHours
                    : task.workerHours
            }
        }
    }

    console.log(memberHours)
}
</script>

<template>
    <TaskSwitcher :tasks="project!.tasks" @update="updateHours" />

    <ProjectChart :user-hours="memberHours" />
</template>

Any guidance on making my chart interactive would be greatly appreciated. I've been grappling with this issue for days without success.

Thank you!

Answer №1

As chartData is a reference, it won't automatically update when props change. You have two options: either use the watch method to monitor prop changes and update chartData, or convert chartData into a computed property that will automatically react to prop changes.

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

Unable to transfer information from the Parent component to the Child component

Can you help me solve this strange issue? I am experiencing a problem where I am passing data from a parent component to a child component using a service method that returns data as Observable<DemoModel>. The issue is that when the child component ...

Troubleshooting Image Upload Problem with Angular, Node.js, Express, and Multer

While trying to implement the functionality of uploading an image, I have been referencing various guides like how to upload image file and display using express nodejs and NodeJS Multer is not working. However, I am facing issues with getting the image to ...

What is the best way to outline this model using typescript?

Here is a JSON model that I am working with: { "loggers" : { "logger1" : { "name" : "logger1", "level" : "DEBUG", "sub_loggers" :{ "logger1.nested_logger1" : { "name": "lo ...

What is the best way to set up a property in a service that will be used by multiple components?

Here is an example of how my service is structured: export class UserService { constructor() {} coords: Coordinates; getPosition() { navigator.geolocation.getCurrentPosition(position => { this.coords = [position.coords.latitude, posit ...

What is the best approach to repurpose a jest test for various implementations of a shared interface?

I'm facing a challenge: describe("Given a config repository", () => { let target: ConfigRepository; beforeEach(() => { target = InMemoryConfigRepository(); }); test("When creating a new config, Then it is ...

Limit an object to only contain interface properties

Suppose we have the following object: o {a : 1, b : 2} and this interface defined as: interface MyInterface { a : number } We are now looking to create a new object that represents the "intersection" of o and the MyInterface: o2 : {a : 1} The mai ...

Is the type check being disregarded within the loop?

I encountered this issue while working with a React component: if (props.someArray){ // I need to typecheck because someArray is of type 'things[] | undefined' props.someArray.forEach((element, i) => { someFunction(i, elem ...

Typescript error points out that the property is not present on the specified type

Note! The issue has been somewhat resolved by using "theme: any" in the code below, but I am seeking a more effective solution. My front-end setup consists of React (v17.0.2) with material-ui (v5.0.0), and I keep encountering this error: The 'palet ...

The parameters provided in TypeScript do not align with any signature of the call target

In JavaScript, a function can be called with any number of parameters. If a parameter is not passed, it will default to undefined without causing an error. Below is a code snippet for reference: function test(a,b){ if(b){console.log(b)} else{console ...

The value of the filename property cannot be determined as it is undefined

Hey everyone, I'm currently working on a project using nestjs and reactjs. I encountered an error when trying to add a document that reads: "Cannot read properties of undefined (reading 'filename') in multer.config.ts" import { diskStorag ...

Tips for preventing redirection in Vue 3 composition API with asynchronous requests

I have successfully configured a submission form to send data to my email and add it to a Google sheet using the method described in this GitHub link. The process worked seamlessly for both purposes. However, I am facing an issue where upon submitting the ...

MUI provides the flexibility to adjust the opacity separately for Chip labels/icons and backgrounds

My objective is to customize the opacity of label/icon and background in MUI Chip. I want the label & icon to have an opacity of 1, while the background should have an opacity of 0.0571. Technologies used in this project include React, TypeScript, Materia ...

When typing declarations are used, they clarify whether the entity being referenced is an Object or

I am currently working on aligning the Dockerode run typings to match the actual implementation. The issue arises when I invoke run as TypeScript consistently identifies the return value as a Promise. It seems that TypeScript is having trouble distinguish ...

OpenTok Angular 6 encountered an error with code TS2314 stating that the generic type 'Promise<T>' needs to have 1 type argument specified

Issue in opentok.d.ts File: Error TS2314 npm version: 6.2.0 node: v8.10.0 Angular CLI: 6.2.3 Operating System: Linux x64 Angular Version: 7.0.0-beta.5 @opentok/client": "^2.14.8 ...

Angular's observables were unable to be subscribed to in either the constructor or ngOnInit() functions

Currently, I am incorporating an observable concept into my application. In this setup, a service is called by component1 to emit an event that is then subscribed to by component 2. Below is the code snippet for reference: Service Code export class Mes ...

Tips for obtaining the OneSignal playerID

When launching the app, I need to store the playerID once the user accepts notifications. This functionality is located within the initializeApp function in the app.component.ts file. While I am able to retrieve the playerID (verified through console.log) ...

Structuring your Angular 6 application and server project

What is the recommended project structure when developing an Angular 6 application and an API server that need to share type definitions? For example: On the client side: this.httpService.get<Hero[]>(apiUrl + '/heroes') On the server si ...

After utilizing the d3-scale function to declare an object, my developer visual suddenly ceases to function

Upon completing a section of a Power BI tutorial, the developer encountered a visual that displayed nothing but a blank page (despite running correctly). Unable to pinpoint the issue, debugging was initiated. The following test code snippet for debugging w ...

Having trouble deleting JavaScript object properties within a loop?

Struggling to comprehend the behavior of this particular piece of javascript code. const devices = searchResult.results.forEach(device => { const temp = Object.keys(device.fields); for(var property in temp) { if(device.fields.hasOwnPro ...

What is the reason for Vue not updating the component after the Pinia state is modified (when deleting an object from an Array)?

When using my deleteHandler function in pinia, I noticed an issue where the users array was not being re-rendered even though the state changed in vue devtools. Interestingly, if I modify values within the array instead of deleting an object from it, Vue ...