【问题标题】:Vue.js v-model with dynamic list of radio buttons具有单选按钮动态列表的 Vue.js v-model
【发布时间】:2020-02-23 17:33:11
【问题描述】:

我正在尝试制作一个可重用的 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. 我可以根据“选项”对象制作单选按钮的动态列表,但找不到将它们绑定到数据模型的方法。下面是代码:

// 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>

任何人都可以提供一些关于我如何前进的建议吗?

【问题讨论】:

    标签: vue.js radio-button v-model vue-directives


    【解决方案1】:

    正如@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>

    【讨论】:

    • 好答案,我使用 [ { ... }, ... ] 等对象的数组/列表创建了一个版本,还有一个使用 Vuetify
    【解决方案2】:

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

          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

    【讨论】:

      【解决方案3】:

      当我研究和玩弄这些答案时,我想出了以下方法:

      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. 数组列表 & &lt;label&gt;) - codepen

      2. 对象列表 & &lt;Label&gt;) - codepen

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

      【讨论】:

      • Vuetify 和 BootstrapVue 都提供灵活的无线电组组件。如果您的项目已经使用其中一个库,那么使用它们是有意义的。但是这个问题是关于从头开始创建一个可重用的无线电组组件。仅为其单选按钮加载一个 Vuetify 大小的库没有多大意义。除此之外,查看您列出的其他两个选项,它们似乎是对已接受答案的抄袭。当您“调整”并重新发布现有解决方案时,您应该正确地对其进行归因并准确阐明您对修改版本的贡献(如果有的话)。
      【解决方案4】:

      基于其他答案之一,我最终得到了 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>
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-10-03
        • 2020-03-31
        • 2023-04-08
        • 2016-06-14
        • 1970-01-01
        • 2020-09-13
        • 2018-01-02
        • 2019-09-20
        相关资源
        最近更新 更多