Атрибут: привязка значения в теге select не обновляется внутри шаблона компонента Vue 3 (Composition API)

У меня есть раскрывающееся меню, в котором параметры перечислены и перемешаны, так что выбранный вариант становится первым. Этот сценарий работает по назначению:

<div id="main">
  <sub-select :subs="data" @comp-update="onShufflePaths($event)"></sub-select>
</div>

. .

 const ui = {
   setup() {
    
    let data = ref('first_second_thrid');
    
    const onShufflePaths = (ind) => {
        let subs = data.value.match(/[^_]+/g);
        const main = subs.splice(ind, 1)[0];
        data.value = [main, ...subs].join('_');
    }
        
     return {
       data, onShufflePaths, 
     };
   },
 };

 const vueApp = createApp(ui);
 
vueApp.component('sub-select', {
  props: ['subs'],
  emits: ['comp-update'],
  setup(props, { emit }) {
  
        let subs = computed(() => props.subs.match(/[^_]+/g));
    
    let subpath = computed(() => '0: ' + subs.value[0]);
    
    function onChange(evt) {
        emit('comp-update', evt.slice(0,1));                      
    } 
    
    return { subs, subpath, onChange };
  },
  template: `
  <select :value="subpath" @change="onChange($event.target.value)">
    <option v-for="(v,k) in subs">{{k}}: {{v}}</option>
  </select> {{subpath}}`
});
 
 vueApp.mount('#main');

Проблема в том, что если я удалю {{subpath}} из шаблона, появится раскрывающееся меню без выбранных по умолчанию параметров. Похоже, что: value = subpath самого по себе недостаточно для обновления переменной subpath при обновлении props, если это явно не указано в шаблоне.

Как заставить его работать?

По сути, мне нужно, чтобы по умолчанию всегда был выбран первый вариант.

Спасибо!

https://jsfiddle.net/tfoller/uy7k1hvr/26/


person user1481126    schedule 02.01.2021    source источник


Ответы (3)


Итак, похоже, что это ошибка в библиотеке.

Решение 1.

оберните тег select в шаблоне другим тегом, например (чтобы это не одинокий корневой элемент в шаблоне):

template: `
  <div><select :value="subpath" @change="onChange($event.target.value)">
    <option v-for="(v,k) in subs">{{k}}: {{v}}</option>
  </select></div>`

Решение 2:

Напишите геттер / сеттер в переменную подпути, поэтому определение компонента выглядит следующим образом:

vueApp.component('sub-select', {
  props: ['subs'],
  emits: ['comp-update'],
  setup(props, { emit }) {
  
        let subs = computed(() => props.subs.match(/[^_]+/g));
    
    let subpath = computed({
      get: () => '0: ' + subs.value[0],
      
      set (value) {
        emit('comp-update', value.slice(0,1))
      }
    });
    
    return { subs, subpath };
  },
  template: `
  <select v-model="subpath">
    <option v-for="(v,k) in subs">{{k}}: {{v}}</option>
  </select>`
});
person user1481126    schedule 10.01.2021

Чтобы по умолчанию был выбран первый вариант, вам нужно указать индекс 0.

Здесь ваш индекс - k из v-for

<option v-for="(v,k) in subs" :selected="k === 0">{{k}}: {{v}}</option>
person LastM4N    schedule 07.01.2021

В select нет v-model, я думаю, что это основная проблема. Другое - непонятно, что тебе делать.

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


    // app.vue
    <template>
      <sub-select v-model="value" :options="options" />
      {{ value }}
    </template>
    
    <script>
    import { ref } from "vue";
    import subSelect from "./components/subSelect.vue";
    
    export default {
      name: "App",
      components: {
        subSelect,
      },
      setup() {
        const value = ref(null);
        const options = ref(["one", "two", "three"]);
    
        return { value, options };
      },
    };
    </script>

видите, что я использовал v-model для привязки значения к sub-select компоненту.

компонент sub-select следующим образом

// subSelect.vue
<template>
  <select v-model="compValue">
    <template v-for="(option, index) in compOptions" :key="index">
      <option :value="option">{{ index }}: {{ option }}</option>
    </template>
  </select>
</template>

<script>
import { computed } from "vue";

export default {
  name: "subSelect",
  props: ["modelValue", "options"],
  setup(props, { emit }) {
    // if value is null then update it to be first option.
    if (props.modelValue === null) {
      emit("update:modelValue", props.options[0]);
    }

    const compValue = computed({
      get: () => props.modelValue,
      set: (v) => emit("update:modelValue", v),
    });
   
    // return selected option first in list/Array.
    const compOptions = computed(() => {
      const selected = props.options.filter((o) => o === compValue.value);
      const notSelected = props.options.filter((o) => o !== compValue.value);
      return [...selected, ...notSelected];
    });

    return { compValue, compOptions };
  },
};
</script>

в компоненте sub-select я сначала проверяю, является ли modelValue null, и если да, установите значение как первый вариант.

а также предоставление compOptions в такой последовательности, чтобы выбранные параметры всегда были первыми в списке параметров выбора.

так что это удовлетворяет

  1. По умолчанию всегда выбирается первый вариант.
  2. Выбранный вариант всегда будет первым в списке вариантов.

проверьте работающий код в codeandbox

редактировать

jsfiddle согласно запросу


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

person ashwin bande    schedule 08.01.2021
comment
@ user1481126 ваш элемент select должен быть привязан к одному ref, а параметры должны быть разными. но похоже, что вы меняете список в соответствии с выбором без привязки значения выбора subpath не имеет установщика. Также длина кода субъективна. это то, что нужно для выполнения требований. Может быть, его можно немного уменьшить, но для этого нужно потрудиться. Пример здесь для сообщества, чтобы легко понять. Опять же важнее, чтобы код был понятен человеку, а не его длина - person ashwin bande; 09.01.2021