1

I'm creating a class in TypeScript to handle my form submission and 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) {
        // post logic
    }
}

This would be used in my Vue components like so:

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')
    }
}

This works, functionally, I can fill in the form and submit data to a server. The problem I am having is with reactivity in Vue.js. Because the properties do not exist on the Form class, the data isn't passed to v-model, it is only updated inside the form once it changes.

This is not a problem unless I am displaying data on the screen from the form. I can live with it for now but I was wondering if there was a way to make Vue recognise the reactive properties on my Form class, or if there was a better way to achieve this functionality without compromising on the type system.

1 Answer 1

1

Well, your operation is much higher than vuejs self do. Vuejs works reactive data with Object.defineProperty in setter to rerender, however, you provide an object which has defined its properties which is not configurable and will not be able to trigger vuejs's reactive system.

To make it work:

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)
    }
}

Notice vm.$forceUpdate which will notify vuejs to rerender when your Form properties change.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.