![](/img/trans.png)
[英]Passing Values Between Non-Parent/Child Relationship Components in Angular 2
[英]VueJS 3 How to trigger methods between two non-parent/child components
我有两个不是彼此的子组件,我只需要通过第二个组件的 function 触发第一个组件中的方法/函数。
第一个组件:
// Component - Product Select
app.component('product-select', {
data() {
return {
options: null
}
},
props: {
modelValue: Array,
},
emits: ['update:modelValue'],
template: `
<div class="ui fluid labeled multiple search selection large dropdown">
<input type="hidden"
name="products"
:value="modelValue"
@change="selectProducts">
<i class="dropdown icon"></i>
<div class="default text">
Select Products
</div>
<div class="menu">
<div v-for="(option, index) in options"
class="item"
v-bind:data-value="option.name">
{{ option.name }}
</div>
</div>
</div>
`,
methods: {
// Add the product selections to the modelValue array
selectProducts(event) {
let value = event.target.value.split(',');
this.$emit('update:modelValue', value);
// Properly empty out the modelValue array if no products are selected
if (/^(""|''|)$/.test(value) || value == null) {
this.$emit('update:modelValue', []);
}
},
updateComponent() {
console.log('Component Updated');
}
},
created: function () {
// Build the products array via the products JSON file
fetch(productsJSON)
.then(response => response.json())
.then(options => {
this.options = options;
});
}
});
第二部分:
// Component - Product Card
app.component('product-card', {
data() {
return {
selectedSymptoms: []
}
},
props: {
modelValue: Array,
product: String
},
template: `
<div class="ui fluid raised card">
<symptoms-modal v-bind:name="product + ' - Modal'"
v-bind:title="product + ' - Symptoms'"
v-on:child-method="updateParent">
</symptoms-modal>
<div class="content">
<div class="header" @click="removeProduct(product)">
{{ product }}
</div>
</div>
<div v-if="selectedSymptoms.length === 0"
class="content">
<div class="description">
No symptoms selected
</div>
</div>
<div v-for="(symptom, index) in selectedSymptoms"
class="content symptom">
<h4 class="ui left floated header">
{{ symptom }}
<div class="sub header">
Rate your {{ symptom }}
</div>
</h4>
<button class="ui right floated icon button"
v-bind:name="symptom + ' - Remove'"
@click="removeSymptom(symptom)">
<i class="close icon"></i>
</button>
<div class="clear"></div>
<input type="text"
class="js-range-slider"
v-bind:name="product + ' - ' + symptom"
value=""
/>
</div>
<div class="ui bottom attached large button"
@click="openModal">
<i class="add icon"></i>
Add/Remove Symptoms
</div>
</div>
`,
methods: {
openModal() {
// Gets the product name
// Product Name
let product = this.product;
// Builds the modal name
// Product Name - Modal
let modal = product + ' - Modal';
// Gets the modal element
// name="Product Name - Modal"
let target = $('[name="' + modal + '"]');
// Assign the currently selected symptoms to a targettable array
let array = this.selectedSymptoms;
// Opens the appropriate modal
$(target).modal({
closable: false,
// Updates all checkboxes when the modal appears if the user
// removes a symptom from the main screen
onShow: function () {
// For each input
$('input', $(target)).each(function () {
// If it is checked
if ($(this).is(':checked')) {
// If it is a currently selected symptom
if (jQuery.inArray(this.name, array) != -1) {
// Is checked and in array, re-check
$(this).prop('checked', true);
} else {
// Is checked and not in array, un-check
$(this).prop('checked', false);
}
} else {
if (jQuery.inArray(this.name, array) != -1) {
// Is not checked and in array, re-check
$(this).prop('checked', true);
} else {
// Is not checked and not in array, do nothing
}
}
});
},
}).modal('show');
},
updateParent(value_from_child) {
// Update the symptoms values from the modal
this.selectedSymptoms = value_from_child;
},
removeSymptom(symptom) {
this.selectedSymptoms.splice($.inArray(symptom, this.selectedSymptoms), 1);
},
removeProduct(product) {
this.$root.selectedProducts.splice($.inArray(product, this.$root.selectedProducts), 1);
}
},
updated() {
// Add custom range input functionality
$(".js-range-slider").ionRangeSlider({
skin: "round",
grid: false,
min: 1,
max: 5,
from: 2,
step: 1,
hide_min_max: true,
values: [
"1 - Adverse Reaction", "2 - No Change", "3 - Partial Resolution", "4 - Significant Resolution", "5 - Total Resolution"
],
onChange: function (data) {
// Name Attribute
// data.input[0].attributes.name.nodeValue
// Input Value
// data.from_value
}
});
}
});
在第一个组件中,我有 function updateComponent()
这是我需要触发的。 在第二个组件中,我有 function removeProduct()
,它需要触发updateComponent()
function。
我尝试过使用 $refs 并且它根本不起作用,并且据我了解,发出事件仅适用于子>父组件。
有几种方法可以做到这一点,实现取决于你的约束。
首先,vue3 不再像 Vue2 那样支持事件总线。 这意味着在组件冒泡之外侦听的事件不再是库功能。 相反,他们建议使用第 3 方选项
在 Vue 3 中,不再可能使用这些 API 从组件内监听组件自己发出的事件,该用例没有迁移路径。
但是 eventHub 模式可以通过使用实现事件发射器接口的外部库来替换,例如mitt或tiny-emitter 。
底线是它缩小了您对三种“策略”的选择范围
watch
(或反应性的某些部分)来执行 function。等式的另一部分是交付方法。
您可以设置一个包含 singleton 参考的文件,这将允许任何策略。
创建一个 pubsub 实例,然后您可以从任何地方收听和发出
// pubsub.js
import mitt from 'mitt'
export const emitter = mitt();
或者,如果您只想传递 function,您可以将其包装起来,实质上是创建一个 function,其中包含执行另一个 function 的指令。
// singleton.js
let updateComponentProxy = () => {};
export const setUpdateComponentProxy = (callback)=>updateComponentProxy=callback;
// component1
created(){
setUpdateComponentProxy(()=>{this.updateComponent();})
}
// component2
// ..on some trigger
updateComponentProxy()
这是一个非常丑陋的实现,但它有效,并且在某些情况下可能是合适的
第三个选项是使用反应性。 您可以通过使用 vuex 或它的 diy 超级配对版本来做到这一点。
// mystore.js
import {ref} from 'vue';
export const updateComponentCount = ref(0);
export const pushUpdateComponentCount = () => updateComponentCount.value++;
// component1
import {watch} from 'vue';
import {updateComponentCount} from 'mystore.js';
created(){
watch(updateComponentCount, this.updateComponent}
}
// component2
import {updateComponentCount} from 'mystore.js';
// ..on some trigger
pushUpdateComponentCount();
这将在updateComponentCount
的值更改时执行updateComponent
function。 您可以使用vuex
做类似的事情,因为它不会(通常)设置为在组件中运行 function ,但在商店中提供一些变量会触发您要听的更改。 这个例子也使用false
一个计数器,但你甚至可以在true
之间切换,因为重要的不是值,而是突变。
如果您尝试在非直系子女和父母之间传递信息,但属于同一“祖先”,则提供/注入功能。
这是一种传递道具的方式,无需将它们从父母传递给孩子,只需让任何孩子都可以访问它。 然后,您可以使用任何策略。 然而有一个警告,如果你将它分配给根组件,它对所有组件都可用,这比其他策略更适合某些策略。
如果您将某些内容分配给app.config.globalProperties
object(其中 app 是根组件实例)上的某个键,则您可以从任何子组件访问该键。 例如
import mitt from 'mitt';
const app = createApp(App)
app.config.globalProperties.emitter = mitt();
可以通过
// component 1
created(){
this.emitter.on('removeProduct', this.updateComponent())
}
// component 2
removeProduct(product) {
this.$root.selectedProducts.splice($.inArray(product, this.$root.selectedProducts), 1);
this.emitter.emit('removeProduct')
}
如果你想在 vue3 setup()
function 中使用,你需要使用getCurrentInstance().appContext.config.globalProperties.emitter
来访问它,因为组件实例不在上下文中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.