Javascript v-model和v-bind单选按钮上的值出现Vue JS prop错误="$attrs";

Javascript v-model和v-bind单选按钮上的值出现Vue JS prop错误="$attrs";,javascript,vue.js,Javascript,Vue.js,我有一种奇怪的行为,我无法控制 我有一个简单的单选按钮组件,用作实际单选按钮的“包装器” 在这个组件上,我有inheritAttrs:false并在元素本身上使用v-bind=“$attrs”,这样我就可以使用v-model和value等 但是,在选择单选按钮时,会抛出一个错误,即prop值无效(因为它是一个事件而不是字符串),有趣的是,我注意到在初始渲染时,Vue Devtools中prop值为空 我只是想让这些单选按钮用所选单选按钮的字符串值更新location的父级数据对象值 我不知道我到

我有一种奇怪的行为,我无法控制

我有一个简单的单选按钮组件,用作实际单选按钮的“包装器”

在这个组件上,我有
inheritAttrs:false
并在元素本身上使用
v-bind=“$attrs”
,这样我就可以使用v-model和value等

但是,在选择单选按钮时,会抛出一个错误,即prop值无效(因为它是一个事件而不是字符串),有趣的是,我注意到在初始渲染时,Vue Devtools中prop值为空

我只是想让这些单选按钮用所选单选按钮的字符串值更新
location
的父级数据对象值

我不知道我到底错在哪里。非常感谢您的帮助

问题的示例项目:

FormMain.vue

<template>
  <div>
    <p>Location: {{ location }}</p>
    <form-radio
      id="location-chicago"
      v-model="location"
      value="Chicago"
      name="location"
      label="Chicago"
      @change="changed"
    />
    <form-radio
      id="location-london"
      v-model="location"
      value="London"
      name="location"
      label="London"
      @change="changed"
    />
  </div>
</template>

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

export default {
  name: "FormMain",
  components: {
    FormRadio
  },
  data() {
    return {
      location: ""
    };
  },
  methods: {
    changed(e) {
      console.log("Change handler says...");
      console.log(e);
    }
  }
};
</script>
<template>
  <div>
    <label :for="id">
      {{ label }}
      <input
        :id="id"
        type="radio"
        :value="value"
        v-on="listeners"
        v-bind="$attrs"
      >
    </label>
  </div>
</template>

<script>
export default {
  name: "FormRadio",
  inheritAttrs: false,
  props: {
    id: {
      type: String,
      required: true
    },
    label: {
      type: String,
      required: true
    },
    value: {
      type: String,
      required: true
    }
  },
  computed: {
    listeners() {
      return {
        ...this.$listeners,
        change: event => {
          console.log("Change event says...");
          console.log(event.target.value);
          this.$emit("change", event.target.value);
        }
      };
    }
  }
};
</script>
<form-radio
  id="location-chicago"
  v-model="location"
  radio-value="Chicago"
  name="location"
  label="Chicago"
  @change="changed"
/>
<form-radio
  id="location-london"
  v-model="location"
  radio-value="London"
  name="location"
  label="London"
  @change="changed"
/>
<template>
  <div>
    <label :for="(id) ? `field-${id}` : false">
      {{ label }}
      <input
        :id="`field-${id}`"
        type="radio"
        :value="radioValue"
        v-on="listeners"
        v-bind="$attrs"
      >
    </label>
  </div>
</template>

<script>
export default {
  name: "FormRadio",
  inheritAttrs: false,
  props: {
    id: {
      type: String,
      required: true
    },
    label: {
      type: String,
      required: true
    },
    radioValue: {
      type: String,
      required: true
    }
  },
  computed: {
    listeners() {
      return {
        ...this.$listeners,
        input: event => {
          console.log("input event says...");
          console.log(event.target.value);
          this.$emit("input", event.target.value);
        },
        change: event => {
          console.log("Change event says...");
          console.log(event.target.value);
          this.$emit("change", event.target.value);
        }
      };
    }
  }
};
</script>

位置:{{Location}

从“/FormRadio.vue”导入FormRadio; 导出默认值{ 名称:"FormMain",, 组成部分:{ FormRadio }, 数据(){ 返回{ 地点:“ }; }, 方法:{ 更改(e){ log(“更改处理程序说…”); 控制台日志(e); } } };
FormRadio.vue

<template>
  <div>
    <p>Location: {{ location }}</p>
    <form-radio
      id="location-chicago"
      v-model="location"
      value="Chicago"
      name="location"
      label="Chicago"
      @change="changed"
    />
    <form-radio
      id="location-london"
      v-model="location"
      value="London"
      name="location"
      label="London"
      @change="changed"
    />
  </div>
</template>

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

export default {
  name: "FormMain",
  components: {
    FormRadio
  },
  data() {
    return {
      location: ""
    };
  },
  methods: {
    changed(e) {
      console.log("Change handler says...");
      console.log(e);
    }
  }
};
</script>
<template>
  <div>
    <label :for="id">
      {{ label }}
      <input
        :id="id"
        type="radio"
        :value="value"
        v-on="listeners"
        v-bind="$attrs"
      >
    </label>
  </div>
</template>

<script>
export default {
  name: "FormRadio",
  inheritAttrs: false,
  props: {
    id: {
      type: String,
      required: true
    },
    label: {
      type: String,
      required: true
    },
    value: {
      type: String,
      required: true
    }
  },
  computed: {
    listeners() {
      return {
        ...this.$listeners,
        change: event => {
          console.log("Change event says...");
          console.log(event.target.value);
          this.$emit("change", event.target.value);
        }
      };
    }
  }
};
</script>
<form-radio
  id="location-chicago"
  v-model="location"
  radio-value="Chicago"
  name="location"
  label="Chicago"
  @change="changed"
/>
<form-radio
  id="location-london"
  v-model="location"
  radio-value="London"
  name="location"
  label="London"
  @change="changed"
/>
<template>
  <div>
    <label :for="(id) ? `field-${id}` : false">
      {{ label }}
      <input
        :id="`field-${id}`"
        type="radio"
        :value="radioValue"
        v-on="listeners"
        v-bind="$attrs"
      >
    </label>
  </div>
</template>

<script>
export default {
  name: "FormRadio",
  inheritAttrs: false,
  props: {
    id: {
      type: String,
      required: true
    },
    label: {
      type: String,
      required: true
    },
    radioValue: {
      type: String,
      required: true
    }
  },
  computed: {
    listeners() {
      return {
        ...this.$listeners,
        input: event => {
          console.log("input event says...");
          console.log(event.target.value);
          this.$emit("input", event.target.value);
        },
        change: event => {
          console.log("Change event says...");
          console.log(event.target.value);
          this.$emit("change", event.target.value);
        }
      };
    }
  }
};
</script>

{{label}}
导出默认值{
名称:“FormRadio”,
继承属性:false,
道具:{
身份证:{
类型:字符串,
必填项:true
},
标签:{
类型:字符串,
必填项:true
},
价值:{
类型:字符串,
必填项:true
}
},
计算:{
听众(){
返回{
…这.$listeners,
更改:事件=>{
log(“更改事件显示…”);
日志(event.target.value);
这是.emit(“change”,event.target.value);
}
};
}
}
};

我从子组件调用中完全删除了v-model(这是冲突的)

更新:链接到更新的

编辑

<script>
export default {
  name: "FormRadio",
  inheritAttrs: false,
  props: {
    id: {
      type: String,
      required: true
    },
    label: {
      type: String,
      required: true
    }
  },
  // customize the event/prop pair accepted by v-model
  model: {
    prop: "radioModel",
    event: "radio-select"
  },
  computed: {
    listeners() {
      return {
        ...this.$listeners,
        change: event => {
          console.log("Change event says...");
          console.log(event.target.value);
          // emit the custom event to update the v-model value
          this.$emit("radio-select", event.target.value);
          // the change event that the parent was listening for
          this.$emit("change", event.target.value);
        }
      };
    }
  }
};
</script>
找到了一篇描述组件的
模型
属性的简洁文章。基本上,它允许您定制
v-model
的工作方式。使用此选项,
FormMain.vue
将不必更改。只需从
FormRadio
中删除value prop,并使用您自己的定义添加model属性

见更新:

格式广播脚本

<script>
export default {
  name: "FormRadio",
  inheritAttrs: false,
  props: {
    id: {
      type: String,
      required: true
    },
    label: {
      type: String,
      required: true
    }
  },
  // customize the event/prop pair accepted by v-model
  model: {
    prop: "radioModel",
    event: "radio-select"
  },
  computed: {
    listeners() {
      return {
        ...this.$listeners,
        change: event => {
          console.log("Change event says...");
          console.log(event.target.value);
          // emit the custom event to update the v-model value
          this.$emit("radio-select", event.target.value);
          // the change event that the parent was listening for
          this.$emit("change", event.target.value);
        }
      };
    }
  }
};
</script>
输入
事件处理程序将更新v模型

FormRadio.vue

<template>
  <div>
    <p>Location: {{ location }}</p>
    <form-radio
      id="location-chicago"
      v-model="location"
      value="Chicago"
      name="location"
      label="Chicago"
      @change="changed"
    />
    <form-radio
      id="location-london"
      v-model="location"
      value="London"
      name="location"
      label="London"
      @change="changed"
    />
  </div>
</template>

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

export default {
  name: "FormMain",
  components: {
    FormRadio
  },
  data() {
    return {
      location: ""
    };
  },
  methods: {
    changed(e) {
      console.log("Change handler says...");
      console.log(e);
    }
  }
};
</script>
<template>
  <div>
    <label :for="id">
      {{ label }}
      <input
        :id="id"
        type="radio"
        :value="value"
        v-on="listeners"
        v-bind="$attrs"
      >
    </label>
  </div>
</template>

<script>
export default {
  name: "FormRadio",
  inheritAttrs: false,
  props: {
    id: {
      type: String,
      required: true
    },
    label: {
      type: String,
      required: true
    },
    value: {
      type: String,
      required: true
    }
  },
  computed: {
    listeners() {
      return {
        ...this.$listeners,
        change: event => {
          console.log("Change event says...");
          console.log(event.target.value);
          this.$emit("change", event.target.value);
        }
      };
    }
  }
};
</script>
<form-radio
  id="location-chicago"
  v-model="location"
  radio-value="Chicago"
  name="location"
  label="Chicago"
  @change="changed"
/>
<form-radio
  id="location-london"
  v-model="location"
  radio-value="London"
  name="location"
  label="London"
  @change="changed"
/>
<template>
  <div>
    <label :for="(id) ? `field-${id}` : false">
      {{ label }}
      <input
        :id="`field-${id}`"
        type="radio"
        :value="radioValue"
        v-on="listeners"
        v-bind="$attrs"
      >
    </label>
  </div>
</template>

<script>
export default {
  name: "FormRadio",
  inheritAttrs: false,
  props: {
    id: {
      type: String,
      required: true
    },
    label: {
      type: String,
      required: true
    },
    radioValue: {
      type: String,
      required: true
    }
  },
  computed: {
    listeners() {
      return {
        ...this.$listeners,
        input: event => {
          console.log("input event says...");
          console.log(event.target.value);
          this.$emit("input", event.target.value);
        },
        change: event => {
          console.log("Change event says...");
          console.log(event.target.value);
          this.$emit("change", event.target.value);
        }
      };
    }
  }
};
</script>

{{label}}
导出默认值{
名称:“FormRadio”,
继承属性:false,
道具:{
身份证:{
类型:字符串,
必填项:true
},
标签:{
类型:字符串,
必填项:true
},
放射性值:{
类型:字符串,
必填项:true
}
},
计算:{
听众(){
返回{
…这.$listeners,
输入:事件=>{
log(“输入事件显示…”);
日志(event.target.value);
这是.emit(“输入”,event.target.value);
},
更改:事件=>{
log(“更改事件显示…”);
日志(event.target.value);
这是.emit(“change”,event.target.value);
}
};
}
}
};

请参见

奇怪的是,您同时设置了
v-model
value
-
v-model
应该是值的真实来源,但是我如何指定每个单选按钮都有自己的值要设置?否则你会怎么做?我会建立一个示例答案…谢谢。如果您可以使用Codesandbox,那就太好了。您试图在两个不同的元素上使用相同的
v-model
,这有点让人困惑,因为它会指定两个元素的值。我猜您是说手动操作?v-model应该可以工作……它可以通过这种方式使用单文本输入。如果我不为单选按钮使用包装器组件,而只是将它们放在具有相同属性的顶层,那么它甚至可以工作。可能是虫子?可能是。我对这件事了解得不够公正。我无法向您展示有效的解决方案,因为我不知道如何给出您的示例,我只能建议您尝试上面的编辑,看看它对您是否比对我更有意义。这在技术上是可行的……但我们失去了v-model为我们这样做的好处。我想我想避免手动同步数据中的属性。我想如果使用您的方法将
location
设置为“Chicago”,您将如何防止这也应用于“London”?我猜这又回到了@DerekPollard所说的我明白了。谢谢你,这很有效。我想这是Vue的一个bug吗?我想这是设计的。据报道,组件包装了其他本机输入类型。我在GitHub上找到了这个