Vue.js 带有动态单选按钮列表的 v-model

Vue.js v-model with dynamic list of radio buttons

我正在尝试制作一个可重用的 vue 单选按钮组件,它将采用变量名称和一个包含标签和值的对象,然后使用 v-for 呈现单选按钮列表。

我已经成功解决了问题的每一半,但未能将它们结合起来:

  1. 我可以制作一组绑定到数据模型的单选按钮,其中按钮是在模板中静态定义的,但我不知道如何使列表动态化。这是相关代码:

//component
const Radio = {
 template: '#test',
  prop: ['value'],
  data () {
    return {
      selected: this.value
    }
  },
  model: {
   prop: 'value',
      event: 'change'
  },
  methods: {
    handleClickInput (e) {
      this.$emit('change', this.selected)
    }
  }
}

//app
var app2 = new Vue({
  el: '#app2',
  data: {  
    door: '',
   doorOptions: {
    'Yes': 1,
    'No': 0,
   }
 },
  components: { Radio, }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app2">
  <radio v-model="door"></radio>
  <p>
    door = {{door}}
  </p>
</div>


<template id="test">
  <div>
    <input type="radio" value="0" v-model="selected" @change="handleClickInput">0
    <input type="radio" value="1" v-model="selected" @change="handleClickInput">1
  </div>
</template>

  1. 我可以根据 "options" 对象制作单选按钮的动态列表,但找不到将它们绑定到数据模型的方法。这是相关代码:

// component
Vue.component('radio-set', {
  template: '#radio-set',
  props: {
   'label-name': '',
   'variable': '',
   'options': '',
  },
  methods: {
    clicked: function(variable, key, value) {
    // none of this is right, it doesn't update the vue data model
  window[variable] = value; //assign the new value to the dynamic variable name
  selected = value;
  this.$emit("click-event", variable) //create the click event for model updating by the parent
    }
  },
})

//app
var app = new Vue({
  el: '#vueApp',
  data: {
    door:'initial value',
  doorOptions: {
    'Yes':1,
    'No':0,
      'Maybe':5,
      'A new option':25
  },
  
  },
  methods: {
  buttonClick: function(p1){
   console.log(p1+': '+window[p1]); //the variable was assigned inside the child component
  }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="vueApp">
 <radio-set 
  label-name="Radio button set" 
  variable="door" 
  :options="doorOptions"
  @click-event="buttonClick" 
 >door: {{door}}
 </radio-set>
</div>

<template id="radio-set">
 <div>
  <label>{{labelName}}:</label>
  <button 
      type="button" 
      v-for="(val, key) in options"
      @click="clicked(variable, key, val)" 
      >
      {{ key }}
  </button>
  </div>
</template>

任何人都可以就我如何前进提供一些指导吗?

首先:对于你的选择,有一个数组会更容易。

      doorOptions: [
        { key: "Yes", value: 1 },
        { key: "No", value: 0 },
        { key: "Maybe", value: 5 },
        { key: "A new option", value: 25 }
      ]
    };

这样你就可以遍历它了。

在您的自定义组件和您的应用程序之间同步所选值的另一个好方法是使用 v-model

A tutorial to implement v-model

这样我们就可以创建一个像这样的可重用组件:

<template>
  <div>
    <label>{{labelName}}:</label>
    <button
      type="button"
      v-for="(val, idx) in options"
      :key="idx"
      @click="clicked(val)"
    >{{ val.key }}</button>
  </div>
</template>

<script>
export default {
  props: ["value", "options", "labelName"],
  methods: {
    clicked(val) {
      this.$emit("input", val);
    }
  }
};
</script>

然后像这样使用它

<template>
  <div id="app">
    <radio-set v-model="selected" label-name="Radio button set" :options="doorOptions"/>
    Selected : {{selected.key}}
  </div>
</template>

<script>
import RadioSet from "./components/RadioSet";

export default {
  name: "App",
  components: {
    RadioSet
  },
  data() {
    return {
      selected: null,
      doorOptions: [
        { key: "Yes", value: 1 },
        { key: "No", value: 0 },
        { key: "Maybe", value: 5 },
        { key: "A new option", value: 25 }
      ]
    };
  }
};
</script>

Live demo

如@PierreSaid 所述,您可以阅读更多关于 v-model 在自定义组件上的用法。

这是使用 input[type="radio"] 并将更改事件发回父组件的另一个示例。

// component
Vue.component('radio-set', {
  template: '#radio-set',
  props: {
    'label-name': '',
    'value': '',
    'options': '',
  }
})

//app
var app = new Vue({
  el: '#vueApp',
  data() {
    return {
      door: null,
      doorOptions: {
        'Yes': 1,
        'No': 0,
        'Maybe': 5,
        'A new option': 25
      }
    };
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="vueApp">
  <radio-set label-name="Radio button set" v-model="door" :options="doorOptions"></radio-set>
  door: {{door}}
</div>

<template id="radio-set">
  <div>
    <div>{{labelName}}:</div>
    <label v-for="(val, key) in options" :key="val">
      <input type="radio" 
        :name="labelName" 
        :value="val" 
        :checked="val == value" 
        @change="$emit('input', val)"> 
      {{ key }}
    </label>
  </div>
</template>

在研究和琢磨这些答案的过程中,我想到了以下方法:

  1. 使用对象数组的首选方法 Vuetify:

new Vue({
    el: '#app',
    vuetify: new Vuetify(),
    data: () => ({
      color: {
        label: "Choose:",
        // Array List
        items: [
          { key: 1, value: "Red", color: "red" },
          { key: 2, value: "Green", color: "green" },
          { key: 3, value: "Blue", color: "blue" }
        ],
        selectedItemKey: 2 // Red
      }
    }),
    computed: {
      selectedColorItem() {
        return this.color.items.find(item => item.key===this.color.selectedItemKey)
      }
    }
  })
<div id="app">
  <v-app id="inspire">
    <v-container fluid mx-4>
          <v-radio-group
            :label="color.label"
            row
            v-model="color.selectedItemKey"
            >
            <v-radio
              :color="item.color"
              :key="item.key"
              :label="item.value"
              :value="item.key"
              v-for="(item, index) in color.items"
              ></v-radio>
          </v-radio-group>
          Color: <span :class=`${selectedColorItem?.color}--text`>
            {{selectedColorItem?.value}}
          </span>(key: {{color.selectedItemKey}})
    </v-container>
  </v-app>
</div>

<link href="https://cdn.jsdelivr.net/npm/@mdi/font@3.x/css/materialdesignicons.min.css" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.0.11/dist/vuetify.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>

  1. 数组列表 & <label>) - codepen

  2. 对象列表 & <Label>) - codepen

  3. 我看了一下使用@Pierre Said 回答中的按钮,但似乎我需要使用一堆 CSS 才能使按钮看起来像单选按钮。

根据其他答案之一,我最终得到了 Vue 3v-model 的工作方式有一些重大变化,即道具名称。 (https://v3.vuejs.org/guide/migration/v-model.html)

样式是基本的,但我想要一些东西来表明活动选择是什么。

<template>
  <div>
    <label>{{ labelName }}:</label>
    <button
      :class="val.key === modelValue ? 'active' : 'inactive'"
      type="button"
      v-for="(val, idx) in options"
      :key="idx"
      @click="clicked(val)"
    >
      {{ val.key }}
    </button>
  </div>
</template>

<script>
export default {
  props: ["modelValue", "options", "labelName"],
  methods: {
    clicked(val) {
      console.log("emitting click", val.key);
      this.$emit("update:modelValue", val.key);
    },
  }
};
</script>

<style scoped>
button {
  padding: 10px;
  margin: 5px;
}
.active {
  background-color: #42b983;
}
.inactive {
  background-color: lightgray;
}
</style>

它以同样的方式使用。我只是将一个基本数组映射到 {key, value} 因为这就是我所需要的,但是做一些更明确的事情会很容易。

<template>
    <radio-set
      label-name="On / Off"
      v-model="myValue"
      :options="options.radioOptions"
    ></radio-set>
</template>


<script>
import RadioSet from "./RadioSet.vue";

let options = {
  radioOptions: ["on", "off"].map((x) => {
    return { key: x, val: x };
  }),
};

export default {
  name: "MyComponent",
  components: {
    RadioSet,
  },
  data: () => {
    return {
      options,
      myValue: "unknown",
    };
  },
};
</script>