как заставить el-select и v-model работать вместе при извлечении настраиваемого компонента

Я использую el-select для создания выбранного компонента. Что-то вроде этого:

<template>
    //omitted code
    <el-select v-model="filterForm.client"
                    filterable
                    remote
                    placeholder="Please enter a keyword"
                    :remote-method="filterClients"
                    :loading="loading">
                <el-option
                        v-for="item in clientCandidates"
                        :key="item._id"
                        :label="item.name"
                        :value="item._id">
                </el-option>
            </el-select>
</template>
<scripts>
    export default {
        data() {
           filterForm: {
                client: ''
           },
           clientCandidates: [],
           loading: false
        },
        methods: {
            filterClients(query) {
                if (query !== '') {
                    this.loading = true;
                    setTimeout(() => {
                        this.loading = false;
                        this.clientCandidates = [{_id: '1', name: 'foo'}, {_id: '2', name: 'bar'}];
                    }, 200);
                } else {
                    this.clientCandidates = [];
                }
            }
        }
    }
</scripts>

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

Согласно руководству,

v-model="fullName"

эквивалентно

v-bind:value="fullName"
v-on:input="$emit('input', $event)"

Итак, я извлек компонент выбора следующим образом:

<template>
<el-select
        v-bind:value="clientId"
        v-on:input="$emit('input', $event)"
        placeholder="Filter by short name"
        filterable="true"
        remote="true"
        :remote-method="filter"
        :loading="loading">
    <el-option
            v-for="item in clients"
            :key="item._id"
            :label="item.name"
            :value="item._id">
    </el-option>
</el-select>
</template>
<scripts>
export default {
    props: {
        clientId: {
            type: String,
            required: true
        }
    },
    data() {
        return {
            clients: [],
            loading: false,
        }
    },
    methods: {
        filter(query) {
            if (query !== '') {
                this.loading = true;
                setTimeout(() => {
                    this.loading = false;
                    this.clients = [{_id: '1', name: 'foo'}, {_id: '2', name: 'bar'}];
                }, 200);
            } else {
                this.clients = [];
            }
        }
    }
}
</scripts>

А родительский компонент выглядит так:

<select-client v-model="filterForm.clientId"></select-client>

Выпадающий список выбора работает нормально, но, к сожалению, при выборе не отображается выбранный мной вариант, он остается пустым после того, как я выберу вариант. Я подозреваю, что, может быть, мне стоит переключить v-on:input на 'v-on: change', но это тоже не работает.

ОБНОВЛЕНИЕ Я создал простой пример, вы можете его клонировать здесь, пожалуйста, проверьте ветку el-select-as-component. Бегать

npm install
npm run dev

Вы увидите простую страницу с 3 видами выбора:
Левая - это настраиваемый компонент, написанный на необработанном выборе, он работает нормально.
Средняя - это настраиваемый компонент, написанный на el-select, раскрывающийся список остается пустым, но вы можете увидеть filterForm.elClientId в консоли после того, как нажмете кнопку Filter. Вот почему я задаю этот вопрос.
Правильный - простой el-select, он отлично работает.


person Yugang Zhou    schedule 19.06.2018    source источник
comment
MCVE, пожалуйста?   -  person Jacob Goh    schedule 19.06.2018
comment
@JacobGoh спасибо, я обновил вопрос с помощью образца github.   -  person Yugang Zhou    schedule 19.06.2018
comment
Я форк вашего репо и сделаю вам пиар, когда закончу   -  person Existe Deja    schedule 20.06.2018
comment
PR здесь ›github.com/Hippoom/element-ui-questions/pull/1   -  person Existe Deja    schedule 20.06.2018


Ответы (1)


В руководстве говорится, что v-model эквивалентно v-bind:value и v-on:input, но если вы присмотритесь, в функции прослушивателя переменная привязанная устанавливается с помощью свойства события. То, что вы делаете в вашем примере, не то же самое, в вашем слушателе вы генерируете другое событие. Если вы не поймаете это новое событие, ваше значение никогда не будет установлено.

Другое дело, что вы не можете изменять реквизит, вы должны рассматривать его как переменную только для чтения.

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

<template>
  <el-select
    :value="selected"
    @input="dispatch"
    placeholder="Filter by short name"
    :filterable="true"
    :remote="true"
    :remote-method="filter"
    :loading="loading">
    <el-option
      v-for="item in clients"
      :key="item._id"
      :label="item.name"
      :value="item._id">
    </el-option>
  </el-select>
</template>

<script>
export default {
  name: 'SelectClient',

  data() {
    return {
      selected: '',
      clients: [],
      loading: false,
    }
  },

  methods: {
    filter(query) {
      if (query !== '') {
        this.loading = true;
        setTimeout(() => {
          this.loading = false
          this.clients = [{_id: '1', name: 'foo'}, {_id: '2', name: 'bar'}]
        }, 200)
      } else {
        this.clients = []
      }
    },

    dispatch (e) {
      this.$emit('input', e)
      this.selected = e
    }
  }
}
</script>

NB: шаблон v-model + watch тоже подойдет. Важно $emit событие ввода, чтобы v-model в родительском элементе был обновлен.

А в своем родителе вы можете использовать этот компонент следующим образом: <select-client v-model="clientId"/>.

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

<template lang="html">
  <select
    v-model="clientId">
    <option
      disabled
      value="">Please select one</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
</template>

<script>
export default {
  data () {
    return {
      clientId: ''
    }
  },

  watch: {
    clientId (newValue) {
      // Do something else here if you want then commit it
      // Of course, listen for the 'setClientId' mutation in your store
      this.$store.commit('setClientId', newValue)
    }
  }
}
</script>

Затем в других ваших компонентах вы можете прослушивать значение $store.state.clientId.

person Existe Deja    schedule 19.06.2018
comment
Спасибо за ваш ответ. Я пробовал ваш код, не работает. Консоль сообщает мне undefined selectedOptions, я думаю, el-select преобразовать необработанное событие, и поэтому event.target больше не существует. Я новичок в Frontend, поэтому могу ошибиться :-( - person Yugang Zhou; 19.06.2018
comment
Просто примечание: thethis.selected = e в dispatch также очень важен, и я считаю, что <el-select/> не очень хорошо реализован, потому что для <el-input/> мне не нужно явно устанавливать this.value=e - person Yugang Zhou; 22.06.2018
comment
Спасибо. Искал точно такое же решение ... И еще пользуюсь Element UI. Компонент el-select немного сбивает с толку, потому что вы не знаете, действительно ли это el-select для запуска события ввода или el-option. На самом деле я думаю, что они оба устраивают соревнования, но лучший способ - смотреть только на высшем уровне. Я взглянул на исходный код element-ui, там тоже есть хороший способ распространения событий, но код может выглядеть немного сложноватым, там есть и другие вещи. Может быть, я изучу этот код еще немного, когда у меня будет больше времени (иначе .. никогда; P) - person funder7; 29.07.2019
comment
Большое спасибо! Я создавал подобный компонент и внезапно обнаружил, что когда вы удаляете или устанавливаете значение с помощью кода, оно не обновляется в самом компоненте filterForm.clientId = null, я имею в виду переменную selected. Чтобы исправить этот сценарий, мне пришлось добавить часы в сам компонент, например: watch:{ 'value':function(pnew, pold){ if(pnew==null) this.selected=null; } } - person Manuel Lazo; 05.04.2021