从子组件中修改 props.value
Posted
技术标签:
【中文标题】从子组件中修改 props.value【英文标题】:Modify props.value from within child component 【发布时间】: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 我应该如何镜像它?我遇到的问题是父更改选择>子检测道具更改并更新数据>子发出事件,因为数据更改>父检测事件并更新选择并且循环无限重复。
阅读同步修饰符:vuejs.org/v2/guide/components.html#sync-Modifier
@btl 我不太确定这对我有什么帮助,因为在那个例子中,状态似乎只存在于父母身上。
【参考方案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 个输入道具
数据中有另一个变量
在创建时,将传入的 prop 添加到数据变量中
使用观察者,观察传入的道具的变化
在组件内部发生更改时,将更改发送出去
演示:
'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 请重新创建一个示例,我可以帮忙看看 :)以上是关于从子组件中修改 props.value的主要内容,如果未能解决你的问题,请参考以下文章