Exploring the power of Vue.js reactivity using Object.defineProperty in a TypeScript environment

Currently, I am in the process of developing a TypeScript class to manage form submissions and handle server errors:

export default class Form<Model> {
    private readonly original: Model
    private changes: Partial<Model>

    constructor(data: Model) {
        this.original = data
        Object.keys(this.original).forEach(key => this.initialiseData(key))
    }

    private initialiseData(key) {
        Object.defineProperty(this, key, {
            get: () => this.data[key],
            set: (value) => this.changes[key] = value
        }
    }

    get data(): Model {
        return Object.assign({}, this.original, this.changes)
    }

    post(url: string) {
        // logic for posting data
    }
}

In Vue components, the class would be utilized as follows:

import { Component, Vue } from 'vue-property-decorator'
import Form from '~/src/vform'

interface LoginForm {
    email: string
    password: string
}

@Component
export default class LoginView extends Vue {
    form: Form<LoginForm> = new Form<LoginForm>({
        email: '',
        password: ''
    })

    async submit() {
        await this.form.post('/auth/login')
    }
}

The functionality works correctly - I can input data into the form and send it to the server. However, an issue arises with reactivity within Vue.js. Since the properties do not exist on the Form class, the data is not picked up by v-model until it changes.

This mainly becomes problematic when displaying data from the form on the screen. At present, I can manage with this limitation, but I am exploring ways to make Vue recognize the reactive properties on my Form class or achieve similar functionality without compromising the type system.

Answer №1

It seems that your approach is different from how Vuejs handles reactive data internally. Vuejs utilizes Object.defineProperty in its setter method to trigger reactivity, whereas you have provided an object with pre-defined properties that are not configurable. This prevents Vuejs from detecting changes in the properties and rerendering accordingly.

To resolve this issue, you can follow the code snippet below:

export default class Form<Model> {

    initialiseData(key) {
        Object.defineProperty(this, key, {
            get: () => this.data[key],
            set: (value) => {
                this.changes[key] = value
                this.onChange(key, value)
            },
        })
    }

    onChange(key, value) {}

}
import _Form from '~/src/vform'

@Component
export default class LoginView extends Vue {
    form: any = {}

    // Lifecycle hook
    mounted() {
        const vm = this
        class Form extends _Form {
          onChange() {
              vm.$forceUpdate()
          }
        }
        const form = new Form({
            email: '',
            password: ''
        })
        this.$set('form', form)
    }
}

Pay attention to vm.$forceUpdate, which prompts Vuejs to rerender whenever there are changes in your Form properties.

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

Vue.js component unable to validate HTML input patterns

Attempting to create HTML forms with the 'pattern' input attribute has been an interesting challenge for me. When implementing this through Vue.js components, I encountered some strange behavior. Check out this fiddle to see it in action. Vue.co ...

What is the best way to implement validation for individual elements within an array that are being displayed on a webpage using a for loop?

Currently, I am utilizing Vue JS within the Vuetify framework to build a dynamic form: <v-text-field v-for="items in itemsArray" :key="items.id" v-model="items.data" :label="items.name" ></v-text-field> ...

Exploring the differences between Angular's @Input and @Output directives and utilizing Injectable Services

When considering the differences between @Input/@Output in parent and child components versus using services that are instantiated only once with dependency injection (@Injectable()), I find myself questioning whether there are any distinctions beyond the ...

Remove a record from Angular 2 Firebase collection

I've been searching extensively for a solution to this problem. Despite following the documentation on AngularFire 2 and Angular 2, I am unable to find a working answer. My goal is simply to delete a specific entry in my Firebase database using its un ...

Is it possible to configure TypeScript (or a tool similar to ESLint) to throw an error when a library returns undefined?

Currently working with knex and sometimes it returns any, for example when forgetting to specify the table type in the query. Are there any recommended tools available to avoid this issue, ensuring all knex queries are typed properly? ...

What is the best way to make a class available in the global namespace within a TypeScript module?

The Issue at Hand Given an existing application with a mixture of modules and global scripts, the goal is to convert one class in a global script (gamma.ts) into a module. This entails adding `export default` before `class gamma {}` in gamma.ts. Additiona ...

Condition not applying in the Modal

I implemented *ngif on a button to show/hide it based on a condition, but it's not working as expected. The button should appear when an item is selected from ng-select. Here is the button code: <button *ngIf="switch" (click)="productSaveInCart() ...

Incompatibility issues between NestJS and socket.io package

After diligently following the documentation, I attempted to install and work with socket.io on nestjs. However, I encountered multiple issues when installing packages. Despite trying different methods to update the package, nothing seemed to resolve the i ...

I am unable to process the information and transform it into a straightforward list

When using ngrx, I am able to retrieve two distinct data sets from storage. private getCatalog() { this.catalogStore.select(CatalogStoreSelectors.selectAllCatalogsLoadSuccess) .pipe( takeWhile(() => this.alive), take(1), ...

How to change class names dynamically in Vue.js?

I am looking for a way to dynamically change the background color based on a review rating using Vue.js. Ideally, I would like to achieve this with the following code: <div class="review" :style="reviewColor(hotel.average)"> In my methods section, ...

What are some ways I can enhance the typography within Material UI?

Currently, I am in the process of developing a custom theme utilizing createMuiTheme. However, my application requires more typography variants than what Material UI provides out of the box. I need to extend the typography so that it aligns with my specifi ...

Return the subclass from the constructor function

class X{ constructor(input: string) { // do things } f() {console.log("X")} } class Y extends X{ constructor(input: string) { // do things } f() {console.log("Y")} } class Z extends X{ con ...

Combining CodeIgniter4 with Vue.js and Webpack's devServer to handle CORS issues

Exploring Vue & CodeIgniter 4, starting from https://github.com/flavea/ci4-vue. No matter what I try, I encounter a persistent CORS error in dev mode: Access to XMLHttpRequest at 'http://example.com/public/api/book/get' from origin &apo ...

Get an angular xml file by utilizing the response from a C# web API download

I am trying to download an XML file from a database using a Web API in C#, which returns the file as byte[]. How can I properly read these bytes and convert them into an XML file on the client side using Angular? Despite attempts with blobs and other metho ...

Is it possible to use non-numeric values as keys in a Typescript Map?

During a lesson: private items: Map<number, string> = new Map(); ... this.items[aNumber] = "hello"; Results in this error message: An element has an any type because the expression of type number cannot be used to index type Map<numbe ...

Having an issue with Vue.js displaying a blank page post running `npm run serve` and configuring it with IIS

Error Message on Empty Page Despite not using History mode and trying numerous solutions, the issue remains unsolved. I initialized a vuejs project with the command vue create my_project. Following that, I attempted to run npm run serve, which successful ...

Ways to position a button at the bottom of a Bootstrap card

In the card layout, I am struggling to position the red button at the bottom of the column despite using margin auto. Can anyone provide a solution for this issue? Thank you in advance! <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/<a ...

The keys within a TypeScript partial object are defined with strict typing

Currently, I am utilizing Mui components along with TypeScript for creating a helper function that can generate extended variants. import { ButtonProps, ButtonPropsSizeOverrides } from "@mui/material"; declare module "@mui/material/Button&q ...

Guide to setting a SetState function within a component (Using NextJS and TypeScript)

I'm currently diving into TypeScript, and I've hit a roadblock when it comes to the correct assignment for my setState function that I need to pass to a component. /index.tsx import { Dispatch, SetStateAction, useState } from "react"; ...

Is the Vue Vite PWA plugin failing to implement runtime caching for APIs?

After adding the PWA plugin to my Vuejs project with Vite, I successfully precached the assets. However, I encountered an issue when trying to implement runtime caching for API requests. Despite setting up the configuration, the runtime cache file doesn&ap ...