Каков хороший шаблон проектирования vue.js, позволяющий избежать изменения свойств напрямую, когда вы не можете использовать вычисляемые свойства

Предположим, у меня в приложении vue.js есть следующий шаблон:

Form.vue:

<template>
    <form>
        <page-1 v-if="step === 1" :firstName="firstName" :lastName="lastName" @submit="step = 2"></page-1>
        <page-2 v-if="step === 2" :email="email" @submit="step = 3"></page-2>
        <page-3 v-if="step === 3" :comments="comments" @submit="submit()"></page-3>
    </form>
</template>

<script>
import Page1 from './Page1.vue';
import Page2 from './Page2.vue';
import Page3 from './Page3.vue';

export default {
    components: {
        Page1,
        Page2,
        Page3
    },

    data() {
        return {
            firstName: '',
            lastName: '',
            email: '',
            comments: '',
            step: 1
        }
    },

    methods: {
        submit() {
            // submit the form
        }
    }
}
</script>

<style>
</style>

Page1.vue:

<template>
    <label>First Name:</label>
    <input type="text" v-model="firstName"></input>
    <label>Last Name:</label>
    <input type="text" v-model="lastName"></input>
    <button @click="$emit('submit')">Next</button>
</template>

<script>
export default {
    props: {
        firstName: '',
        lastName: ''
    }
}
</script>

<style>
</style>

Page2.vue:

<template>
    <label>Email:</label>
    <input type="email" v-model="email"></input>
    <button @click="$emit('submit')">Next</button>
</template>

<script>
export default {
    props: {
        email: ''
    }
}
</script>

<style>
</style>

Page3.vue:

<template>
    <label>Comments:</label>
    <textarea v-model="comments"></textarea>
    <button @click="$emit('submit')">Submit</button>
</template>

<script>
export default {
    props: {
        comments: ''
    }
}
</script>

<style>
</style>

По сути, это форма из трех страниц. Родительский компонент - это форма, а каждая страница - дочерний компонент. Родительский компонент отслеживает поля формы со своими данными и передает их на каждую страницу с помощью свойств. Но это плохой дизайн, потому что я изменяю свойства в каждом дочернем компоненте, назначая их v-модели на каждом входе.

Большинство решений этой проблемы, которые я видел в Google, предлагают использовать вычисляемые свойства, но поскольку я использую v-модель, мне нужно поддерживать двустороннюю привязку, а вычисляемые свойства кажутся только односторонними. Поэтому мне интересно, является ли следующий шаблон для решения этой проблемы, который считается хорошим дизайном:

Form.vue:

<template>
    <form>
        <page-1 v-if="step === 1" :firstName="firstName" :lastName="lastName" @first-name-changed="val => firstName=val" last-name-changed="val => lastName=val" @submit="step = 2"></page-1>
        <page-2 v-if="step === 2" :email="email" @email-changed="val => email=val" @submit="step = 3"></page-2>
        <page-3 v-if="step === 3" :comments="comments" @comments-changed="val => comments=val" @submit="submit()"></page-3>
    </form>
</template>

<script>
import Page1 from './Page1.vue';
import Page2 from './Page2.vue';
import Page3 from './Page3.vue';

export default {
    components: {
        Page1,
        Page2,
        Page3
    },

    data() {
        return {
            firstName: '',
            lastName: '',
            email: '',
            comments: '',
            step: 1
        }
    },

    methods: {
        submit() {
            // submit the form
        }
    }
}
</script>

<style>
</style>

Page1.vue:

<template>
    <label>First Name:</label>
    <input type="text" v-model="firstNameData"></input>
    <label>Last Name:</label>
    <input type="text" v-model="lastNameData"></input>
    <button @click="$emit('submit')">Next</button>
</template>

<script>
export default {
    props: {
        firstName: '',
        lastName: ''
    },

    data() {
        return {
            firstNameData: '',
            lastNameData: ''
        }
    },

    watch: {
        'firstNameData': function() {
            this.$emit('first-name-changed', this.firstNameData);
        },

        'lastNameData': function() {
            this.$emit('last-name-changed', this.lastNameData);
        }
    },

    created() {
        this.firstNameData = this.firstName;
        this.lastNameData = this.lastName;
    }
}
</script>

<style>
</style>

Идея состоит в том, чтобы не повторять тот же шаблон в двух других дочерних компонентах, чтобы скопировать значения реквизитов в переменные данных в дочерних компонентах, привязать переменные данных к входам, следить за изменениями в переменных данных и генерировать резервные копии событий. родителю, когда они меняются. Эти выбросы будут приходить с новыми значениями, и родитель будет назначать эти новые значения своим переменным данных.

Мне не нужно беспокоиться о динамическом изменении свойств, передаваемых каждому дочернему компоненту. Единственная причина, по которой я хочу передать их дочерним компонентам, заключается в том, что пользователь может захотеть повторно загрузить форму из черновика (в противном случае не было бы необходимости в реквизитах вообще).

У меня вопрос: хороший ли это шаблон дизайна?


person gib65    schedule 07.06.2020    source источник
comment
можно не использовать vuex?   -  person A. L    schedule 07.06.2020
comment
Согласен, может помочь vuex.   -  person Bondsmith    schedule 07.06.2020


Ответы (1)


Вот несколько способов справиться с этим сценарием, Vuex - хороший вариант, но для трех страниц, я думаю, не нужен.

Использование модификатора .sync

<Child :name.sync="childName"></Child>

А в дочернем компоненте вы можете обновить значение с помощью события

this.$emit('update:name', newName);

вы можете прослушивать событие от дочернего элемента и обновлять свойство родительских данных

//parent component
<div>
  <input-name @updateName="eventToUpdateName" /> <!--child component-->
</div>
...
data: () => ({ nameFromChild: '' )},
methods: {
  eventToUpdateName(value) {
    this.nameFromChild = value; // Update from child value emitted
  }
}
...

И в дочернем компоненте

// Child component
<input v-model="name" />
...
data: () => ({ name: '' }),
// watch for changes in the name property and emit an event, and pass the value to the parent
watch: { name() { this.$emit('updateName', this.name } }
...

Кроме того, вы можете использовать директиву v-model и генерировать событие input от дочернего элемента.

//parent component
<div>
  <input-name v-model="nameFromChild" /> <!--child component-->
</div>
...
data: () => ({ nameFromChild: '' )}
...

Теперь в дочернем компоненте у вас может быть

// Child component
<div>
  <input v-model="name" />
</div>
data: () => ({ name: '' }),
props: { value: { type: String, default: '' },
created() { this.name = this.value }, // You can receive a default value
watch: { name() { this.$emit('input', this.name } }
...
person Andres Foronda    schedule 07.06.2020