【问题标题】:Modify props.value from within child component从子组件中修改 props.value
【发布时间】:2018-07-12 12:21:00
【问题描述】:

我是 Vue 的新手,正在尝试构建一个“下拉”组件。我想从这样的父组件中使用它:

<my-dropdown v-model="selection"></my-dropdown>

其中selection 在父级上存储为data,应更新以反映用户的选择。为此,我相信我的下拉组件需要一个 value 属性,并且它需要在选择更改时发出 input 事件。

但是,我还想从子本身内部修改value,因为我希望能够自己使用下拉组件(我需要修改它,否则 UI 将不会更新以反映如果组件单独使用,则新选择的值)。

有没有一种方法可以像上面那样与v-model 绑定,但也可以从孩子内部修改值(似乎我不能,因为value 是一个道具,孩子不能修改自己的道具)。

【问题讨论】:

  • 无论如何你都不能修改 prop,但是你可以在组件内部设置一个镜像数据,并在没有 v-model 传递给它的情况下使用该数据更新 UI。跨度>
  • @wxsm 我应该如何镜像它?我遇到的问题是父更改选择>子检测道具更改并更新数据>子发出事件,因为数据更改>父检测事件并更新选择并且循环无限重复。
  • @btl 我不太确定这对我有什么帮助,因为在那个例子中,状态似乎只存在于父母身上。

标签: vue.js vuejs2


【解决方案1】:

您需要为处理输入/值值的本地值提供计算属性代理。

props: {
  value: {
    required: true,
  }
},
computed: {
  mySelection: {
    get() {
      return this.value;
    },
    set(v) {
      this.$emit('input', v)
    }
  }
}

现在您可以将您的模板设置为使用 mySelection 值来管理此组件内的数据,并且随着它的更改,数据会正确发出并且始终与 v-model (selected) 同步在父级中使用它。

【讨论】:

  • @Ferrybig 你是对的。它应该发出v。我的错。我已经编辑了答案。
  • 这假设我有一个父组件来接收事件并更新道具。
  • @Flash 你不是在另一个组件中使用这个组件吗?
  • 这不是最佳答案吗?它以一种非常干净、可重复使用的模式以最优雅的方式解决了最初的问题。
【解决方案2】:

Vue 的理念是:“道具下降,事件上升”。它甚至在文档中这样说:Composing Components

组件旨在一起使用,最常见于 父子关系:组件 A 可以自己使用组件 B 模板。他们不可避免地需要相互交流: 父母可能需要将数据向下传递给孩子,孩子可能需要 通知父母孩子发生的事情。然而, 保持父母和孩子的关系也很重要 通过明确定义的接口尽可能地解耦。这确保 每个组件的代码都可以相对编写和推理 隔离,从而使它们更易于维护并且可能更容易 重复使用。

在Vue中,父子组件关系可以概括为 道具下降,事件上升。父母通过以下方式将数据传递给孩子 props,子节点通过事件向父节点发送消息。让我们 看看他们接下来如何工作。

不要尝试从子组件中修改值。告诉父组件一些事情,如果父母关心,它可以做一些事情。让子组件改变事物会给孩子带来太多的责任。

【讨论】:

    【解决方案3】:

    您可以使用自定义表单输入组件

    Form Input Components using Custom Events

    基本上,您的自定义组件应该接受 value 属性并在值更改时发出 input 事件

    【讨论】:

      【解决方案4】:

      您可以使用以下模式:

      1. 接受 1 个输入道具
      2. 数据中有另一个变量
      3. 在创建时,将传入的 prop 添加到数据变量中
      4. 使用观察者,观察传入的道具的变化
      5. 在组件内部发生更改时,将更改发送出去

      演示:

      'use strict';
      Vue.component('prop-test', {
        template: "#prop-test-template",
        props: {
          value: { // value is the default prop used by v-model
            required: true,
            type: String,
          },
        },
        data() {
          return {
            dataObject: undefined,
            // For testing purposes:
            receiveData: true,
            sendData: true,
          };
        },
        created() {
          this.dataObject = this.value;
        },
        watch: {
          value() {
            // `If` is only here for testing purposes
            if(this.receiveData)
            this.dataObject = this.value;
          },
          dataObject() {
            // `If` is only here for testing purposes
            if(this.sendData)
            this.$emit('input', this.dataObject);
          },
        },
      });
      
      var app = new Vue({
        el: '#app',
        data: {
          test: 'c',
        },
      });
      <script src="https://unpkg.com/vue@2.0.1/dist/vue.js"></script>
      <script type="text/x-template" id="prop-test-template">
        <fieldset>
          <select v-model="dataObject">
            <option value="a">a</option>
            <option value="b">b</option>
            <option value="c">c</option>
            <option value="d">d</option>
            <option value="e">e</option>
            <option value="f">f</option>
            <option value="g">g</option>
            <option value="h">h</option>
            <option value="i">i</option>
            <option value="j">j</option>
          </select>
          <!-- For testing purposed only: -->
          <br>
          <label>
            <input type="checkbox" v-model="receiveData">
            Receive updates
          </label>
          <br>
          <label>
            <input type="checkbox" v-model="sendData">
            Send updates
          </label>
          <!--/ For testing purposed only: -->
        </fieldset>
      </script>
      <div id="app">
        <prop-test v-model="test"></prop-test>
        <prop-test v-model="test"></prop-test>
        <prop-test v-model="test"></prop-test>
      </div>

      请注意,此演示有一个功能,您可以关闭每个选择框的事件传播,因此您可以测试值是否在本地正确更新,这当然不是生产所需要的。

      【讨论】:

        【解决方案5】:

        如果您想修改组件内的 prop,我建议您将“默认值”prop 传递给您的组件。以下是我的做法

        <MyDropdownComponent
          :default="defaultValue"
          :options="options"
          v-model="defaultValue"
        />
        

        然后有两个选项可以让我从那里开始 -

        选项 1 - 自定义下拉元素

        当您使用自定义 HTML 时,您将无法设置选定属性。所以你需要对你的方法有创意。 在组件内部,您可以将默认值 prop 设置为组件创建时的数据属性。您可能希望以不同的方式执行此操作,并改用watcher。我已将其添加到下面的示例中。

        export default {
          ...
          data() {
            return {
              selected: '',
            };
          },
          created() {
            this.selected = this.default;
          },
          methods: {
            // This would be fired on change of your dropdown.
            // You'll have to pass the `option` as a param here,
            // So that you can send  that data back somehow
            myChangeMethod(option) {
              this.$emit('input', option);
            },
          },
          watch: {
            default() {
              this.selected = this.default;
            },
          },
        };
        

        $emit 会将数据传回父组件,该组件不会被修改。如果您使用的是标准 select 元素,则无需执行此操作。

        选项 2 - 标准 select

        <template>
          <select
            v-if="options.length > 1"
            v-model="value"
            @change="myChangeMethod"
          >
            <option
              v-for="(option, index) of options"
              :key="option.name"
              :value="option"
              :selected="option === default"
            >{{ option.name }}</option>
          </select> 
        </template>
        
        <script>
        export default {
          ...
          data() {
            return {
              value: '',
            };
          },
          methods: {
            // This would be fired on change of your dropdown
            myChangeMethod() {
              this.$emit('input', this.value);
            },
          },
        };
        </script>
        

        这种方法绝对是最简单的,意味着你需要使用默认的select元素。

        【讨论】:

        • 在这两个选项中,您都无法在父级出于某种原因更新值时对条件做出反应
        • @Ferrybig 实际上在第二个选项中,值会更新:) 第一个示例需要watcher,我提到过编辑:哎呀!我没有发出正确的值!
        • 查看我上面的评论:我遇到的问题是父更改选择 > 子检测道具更改并更新数据 > 子因为数据更改而发出事件 > 父检测事件并更新选择并且循环只是重复无限
        • @Flash 请重新创建一个示例,我可以帮忙看看 :)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-04-04
        • 2015-04-15
        • 2020-03-08
        • 2015-08-29
        • 2018-12-28
        • 1970-01-01
        • 2017-11-09
        相关资源
        最近更新 更多