Двусторонняя привязка в компоненте Vue 2.3

Я понимаю модификатор .sync, возвращаемый в Vue 2.3, и использую его для простого дочернего компонента, который реализует вопрос и ответ с «множественным выбором». Родительский компонент вызывает дочерний элемент следующим образом:

<question
  :stem="What is your favourite colour?"
  :options="['Blue', 'No, wait, aaaaargh!']
  :answer.sync="userChoice"
>

У родительского элемента есть строковый элемент данных userChoice для хранения результата дочернего компонента. Ребенок представляет вопрос и переключатели для вариантов. Основные части ребенка выглядят следующим образом (я использую Quasar, отсюда q-radio):

<template>
  <div>
    <h5>{{stem}}</h5>
    <div class="option" v-for="opt in options">
      <label >
        <q-radio v-model="option" :val="opt.val" @input="handleInput"></q-radio>
        {{opt.text}}
      </label>
    </div>
  </div>
</template>

export default {
  props: {
    stem: String,
    options: Array,
    answer: String
  },
  data: () => ({
    option: null
  }),
  methods: {
    handleInput () {
      this.$emit('update:answer', this.option)
    }
  }
}

Все это работает нормально, за исключением того факта, что если родительский затем изменяет значение userChoice из-за чего-то еще, происходящего в приложении, дочерний элемент не обновляет переключатели. Пришлось включить этот watch в дочерний элемент:

  watch: {
    answer () {
      this.option = this.answer
    }
  }

Но это кажется немного избыточным, и я беспокоился, что генерация события для обновления родительских данных на самом деле приведет к срабатыванию дочернего события watch. В этом случае это не имело бы никакого эффекта, кроме потери нескольких циклов, но если бы он регистрировал или считал что-либо, это было бы ложным срабатыванием ...

Возможно, это правильное решение для истинного двустороннего связывания (т.е. динамического родительского → дочернего, а также дочернего → родительского). Я что-то пропустил о том, как соединить входящие и исходящие данные с обеих сторон?

Если вам интересно, наиболее распространенным случаем, когда родитель хочет изменить userChoice, будет ответ на кнопку «Очистить ответы», которая вернет userChoice обратно в пустую строку. Это должно привести к отключению всех переключателей.


person dsl101    schedule 06.06.2017    source источник


Ответы (1)


В вашей конструкции были некоторые странности, которые не работали, но в основном answer.sync работает, если вы распространяете их на q-radio компонент, где происходит изменение. Изменение ответа в родительском элементе обрабатывается правильно, но для очистки значений кажется, что вам нужно установить его для объекта, а не для null (я думаю, это потому, что он должен быть назначен).

Обновить Ваша установка options - примечательная вещь, которая не сработала.

Я использую answer в q-radio, чтобы контролировать его отмеченное состояние (v-model имеет особое поведение в радио, поэтому я использую value в сочетании с v-model). Судя по вашему комментарию, похоже, что q-radio хочет иметь значение, которое он может установить. Вы должны иметь возможность сделать это с помощью вычисления на основе answer, которое вы бы использовали вместо своего option элемента данных: get возвращает answer, а set выполняет emit. Я обновил свой фрагмент, чтобы использовать опору val для q-radio плюс вычисленные, которые я описываю. proxyAnswer испускает событие update, чего хочет модификатор .sync. Я также реализовал q-radio, используя вычисленный прокси, но это просто для того, чтобы получить поведение, которое уже должно быть встроено в ваш q-radio.

(То, что я описываю, по сути, это то, что вы делаете с элементом данных и наблюдателем, но вычисление - лучший способ инкапсулировать это).

new Vue({
  el: '#app',
  data: {
    userChoice: null,
    options: ['Blue', 'No, wait, aaaaargh!'].map(v => ({
      value: v,
      text: v
    }))
  },
  components: {
    question: {
      props: {
        stem: String,
        options: Array,
        answer: String
      },
      computed: {
        proxyAnswer: {
          get() {
            return this.answer;
          },
          set(newValue) {
            this.$emit('update:answer', newValue);
          }
        }
      },
      components: {
        qRadio: {
          props: ['value', 'val'],
          computed: {
            proxyValue: {
              get() {
                return this.value;
              },
              set(newValue) {
                this.$emit('input', newValue);
              }
            }
          }
        }
      }
    }
  },
  methods: {
    clearSelection() {
      this.userChoice = {};
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
  <question stem="What is your favourite colour?" :options="options" :answer.sync="userChoice" inline-template>
    <div>
      <h5>{{stem}}</h5>
      <div class="option" v-for="opt in options">
        <div>Answer={{answer && answer.text}}, option={{opt.text}}</div>
        <label>
        <q-radio :val="opt" v-model="proxyAnswer" inline-template>
          <input type="radio" :value="val" v-model="proxyValue">
        </q-radio>
        {{opt.text}}
      </label>
      </div>
    </div>
  </question>
  <button @click="clearSelection">Clear</button>
</div>

person Roy J    schedule 06.06.2017
comment
Мне любопытны странности - я знаю, что не опубликовал весь компонент, но у меня он работает нормально. Возможно, из-за Quasar, в который входят Webpack и Babel? Весь мой код находится в однофайловых компонентах (.vue). Но, что наиболее важно, q-radio - это компонент квазара (quasar-framework.org/components/radio. html). Поэтому я не уверен, что смогу реализовать то, что вы сделали выше - он принимает только v-model и val реквизиты, но вы также указали answer там. И, конечно же, я не могу привязать свой входящий answer к q-radio v-model, что вызывает ошибку «изменение опоры». - person dsl101; 07.06.2017
comment
Но, исходя из того, что вы сказали, я думаю, что это сводится к другому моему вопросу, на который тоже нет очень четкого ответа: stackoverflow.com/questions/43606355/ На самом деле мне нужно передать привязку answer в v-model q-radio. Но на данный момент Vue не обеспечивает бесшовного способа сделать это, я не думаю. - person dsl101; 07.06.2017