Реактивность Vue.js и Object.defineProperty с TypeScript

Я создаю класс в TypeScript для обработки моей отправки формы и ошибок сервера:

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

Это будет использоваться в моих компонентах Vue следующим образом:

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

Функционально это работает, я могу заполнить форму и отправить данные на сервер. У меня проблема с реактивностью в Vue.js. Поскольку свойства не существуют в классе Form, данные не передаются в v-model, они обновляются внутри формы только после ее изменения.

Это не проблема, если я не показываю данные на экране из формы. Сейчас я могу жить с этим, но мне было интересно, есть ли способ заставить Vue распознавать реактивные свойства в моем классе Form, или есть лучший способ реализовать эту функциональность без ущерба для системы типов.


person Andrew Willis    schedule 03.07.2019    source источник


Ответы (1)


Что ж, ваша работа намного выше, чем у vuejs self. Vuejs работает с реактивными данными с Object.defineProperty в установщике для повторной визуализации, однако вы предоставляете объект, который определил свои свойства, который не настраивается и не сможет запускать реактивную систему vuejs.

Чтобы это работало:

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

Обратите внимание на vm.$forceUpdate, который уведомит vuejs о повторной визуализации при изменении свойств вашей формы.

person frustigor    schedule 08.04.2020