具有v-model和v-bind =“ $ attrs”

时间:2019-05-01 15:27:30

标签: javascript vue.js

我遇到了一些奇怪的行为,无法缠住我的头。

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

在此组件上,我拥有inheritAttrs: false并在元素本身上使用v-bind="$attrs",因此可以使用v模型和值等。

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

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

我无法弄清楚我到底要去哪里。任何帮助表示赞赏。

问题的示例项目: https://codesandbox.io/embed/m40y6y10mx

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>

FormRadio.vue

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

2 个答案:

答案 0 :(得分:1)

我从子组件调用中完全删除了v模型(这是有冲突的);

<form-radio
  id="location-chicago"
  value="Chicago"
  name="location"
  label="Chicago"
  @change="changed"
/>
<form-radio
  id="location-london"
  value="London"
  name="location"
  label="London"
  @change="changed"
/>

然后我更新了更改的方法,以包括设置位置变量

methods: {
  changed(e) {
    console.log("Change handler says...");
    console.log(e);
    this.location = e;
  }
}

已更新:链接到已更新的CodeSandbox

答案 1 :(得分:1)

修改

找到了this整洁的文章,描述了组件的model属性。基本上,它允许您自定义v-model的工作方式。使用此,FormMain.vue无需更改。只需从FormRadio中删除值属性,然后使用您自己的定义添加模型属性

查看更新后的codepen

FormRadio脚本

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

编辑前:

如果存在value,Vue似乎会忽略v-model绑定属性。我通过为radio-value之类的值使用自定义属性来解决此问题。

FormMain.vue

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

input事件处理程序将更新v模型。

FormRadio.vue

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

请参见forked codepen