Vue — Использовать дочерний компонент для добавления и обновления

#javascript #vue.js

#javascript #vue.js

Вопрос:

У меня есть довольно простой родительский / дочерний компонент. Я хочу использовать дочерний компонент двумя способами — первый для операции по добавлению новой записи и второй для операции по обновлению объекта.

Я создал эти компоненты:https://codepen.io/anon/pen/bJdjyx Здесь я не использую реквизиты, я синхронизирую значение от родительского элемента к дочернему с помощью пользовательских событий.

добавить шаблон:

 <div id="app">
  <v-app>
    <v-content>
      <v-container grid-list-xl>
        <my-address
          :addresscompany.sync="addressCompany"
          :addressstreet.sync="addressStreet"
          ></my-address>
        <v-btn @click="submit">Submit</v-btn>
      </v-container>
    </v-content>
  </v-app>
</div>

<script type="text/x-template" id="address-template">
    <div>
      <v-text-field
        name="company"
        v-model="addressCompany"
        @change="updateCompany()">
        </v-text-field>
      <v-text-field
        name="street"
        v-model="addressStreet"
        @change="updateStreet()">
        </v-text-field>
    </div>
</script>
  

добавить скрипт:

 let addressComponent = {
  template: '#address-template',

  data() {
    return {
      addressCompany: '',
      addressStreet: '',
    }
  },

  methods: {
    updateCompany () {
      this.$emit('update:addresscompany', this.addressCompany);
    },
        updateStreet () {
      this.$emit('update:addressstreet', this.addressStreet);
    }
  }
};


new Vue({
  el: '#app',
  components: {'my-address' : addressComponent},

  data() {
    return {
      addressCompany: '',
      addressStreet: '',
    }
  },

  methods: {
    submit () {
      console.log('Company '   this.addressCompany);
       console.log('Street '   this.addressStreet);
    }
  }
})
  

Но этот шаблон не работает для случая редактирования, потому что мне нужны реквизиты для передачи значения дочернему элементу. Итак, я придумал это: https://codepen.io/anon/pen/zXGLQG

обновить — Шаблон:

 <div id="app">
  <v-app>
    <v-content>
      <v-container grid-list-xl>
        <my-address
          :addresscompany.sync="addressCompany"
          :addressstreet.sync="addressStreet"
          ></my-address>
        <v-btn @click="submit">Submit</v-btn>
      </v-container>
    </v-content>
  </v-app>
</div>


<script type="text/x-template" id="address-template">
    <div>
      <v-text-field
        name="company"
         :value="addressCompany"
        @change="updateCompany()">
        </v-text-field>
      <v-text-field
        name="street"
         :value="addressStreet"
        @change="updateStreet()">
        </v-text-field>
    </div>
</script>
  

update — Скрипт:

 let addressComponent = {
  template: '#address-template',
  props: ['addressCompany', 'addressStreet'],

  data() {
    return {
    }
  },

  methods: {
    updateCompany () {
      this.$emit('update:addresscompany', this.addressCompany);
    },
        updateStreet () {
      this.$emit('update:addressstreet', this.addressStreet);
    }
  }
};


new Vue({
  el: '#app',
  components: {'my-address' : addressComponent},

  data() {
    return {
      addressCompany: 'Company',
      addressStreet: 'Street',
    }
  },

  methods: {
    submit () {
      console.log('Company '   this.addressCompany);
       console.log('Street '   this.addressStreet);
    }
  }
})
  

Основное отличие в том, что для случая обновления я не использую v-model для дочерних элементов, потому что я не могу напрямую изменять реквизиты. Но при использовании значения : событие обновления не запускается.

Итак, каков правильный способ использования этого дочернего компонента для добавления и обновления? Должен быть стандартный способ Vue перед использованием Vuex для этих целей, есть ли какой-либо?

Спасибо!

Комментарии:

1. Ваш codepen просто постоянно обновляется для меня

2. Вы можете передать данные редактирования компоненту через prop, а затем использовать пользовательское событие для обновления данных.

3. @TahaPaksu Это то, что я делаю во втором примере? Но поскольку я использую:value, это не работает, потому что пользовательское событие не запускается.

4. использовать шину событий для всего приложения?

5. medium.com/@andrejsabrickis/…

Ответ №1:

Я часто использовал следующий шаблон:

Создайте компонент, который является абстракцией вашей сущности, в приведенном ниже примере я использовал Person.vue with firstName и lastName props.

 Vue.component('Person' , {
   template: `
    <div>
      <input type="text"
             :value="firstName"
              @input="$emit('update:firstName', $event.target.value)"/>
      <input type="text"
              :value="lastName"
              @input="$emit('update:lastName', $event.target.value)"/>
    </div>
  `,
  props: {
    firstName: String,
    lastName: String
  }
});
  

Область действия этого компонента строго ограничена привязкой к
Person объект, реагирующий на его изменения и обновляющий родительский, выдает sync модификатор события.

Затем создайте компонент переноса, который будет обрабатывать, если состояние фактического объекта, т.е. Person.js . Его дочернего компонента Person.vue , не волнует, находитесь ли вы в «режиме» добавления или редактирования. Этот компонент отвечает только за реагирование на изменения модели.

Этот компонент должен либо загрузить Person объект из вызова API или хранилища данных (режим обновления), либо создать новый пустой, т.е. new Person() (Режим добавления).

Простой пример приведен ниже, в идеале родительский компонент (в данном случае приложение) должен быть разделен, чтобы уменьшить использование вспомогательных свойств.

 function Person (firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
Vue.component('Person' , {
  template: `
    <div>
      <input type="text"
             :value="firstName"
              @input="$emit('update:firstName', $event.target.value)"/>
      <input type="text"
              :value="lastName"
              @input="$emit('update:lastName', $event.target.value)"/>
    </div>
  `,
  props: {
    firstName: String,
    lastName: String
  }
});
new Vue({
  el: '#app',
  template: `
    <div>
      <!-- Add mode, should be its own component -->
      <section v-if="promptAdd">
        <Person v-bind.sync="newPerson"/>
        <button type="button" @click="onAdded">Add</button>
      </section>
      <!-- Update mode, should be its own component -->
      <section v-else>
        <p>Users:</p>
        <div v-for="(person, index) in arr" :key="index">
          <Person v-bind.sync="person"/>
        </div>
        <button type="button"
                @click="addNew">Add New User</button>
      </section>
    </div>
  `,
  data: () => ({
    promptAdd: false,
    newPerson: undefined,
    arr: [
      new Person('Bob', 'Smith')
    ]
  }),
  methods: {
    addNew() {
      this.newPerson = new Person();
      this.promptAdd = true;
    },
    onAdded () {
      this.arr.push({ ...this.newPerson });
      this.promptAdd = false;
      this.newPerson = undefined;
    }
  }
})  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app"></div>