如何在 Vue 中实现两个任意元素之间的通信?



我目前正在使用 Vue 框架构建一个应用程序,遇到了一个奇怪的问题,到目前为止我无法找到一个很好的解决方案:

我正在尝试做的是将一个类添加到父容器中,以防容器内的特定元素(输入、选择、文本区域等(获得焦点。下面是示例代码:

<div class="form-group placeholder-label">
<label for="desc"><span>Description</span></label>
<div class="input">
<input id="desc" type="text" />
</div>
</div>

当然,在香草JS中,这很容易做到:

const parent = document.querySelector('.placeholder-label');
const input = parent.querySelector('input');
input.addEventListener('focus', (e) => {
parent.classList.add('active');
});

同样,您可以遍历所有.placeholder-label元素并将事件添加到其子输入/选择等中以添加此基本功能。这里有两个活动部件:

  1. 您不知道父元素的类型,只知道它上面有.placeholder-label
  2. 您不知道子元素的类型,只知道它是父元素中的某种 HTML 表单元素。

我可以构建一个 Vue 组件,根据给定子元素的焦点/模糊在给定的父元素上切换类吗?我能想到的最好的方法是为子元素使用插槽,但是我仍然需要为每个父元素构建一个组件。即使将mixin用于重用的部分,与我需要用纯JS编写的五行代码相比,它仍然相当混乱。

我的模板:

<template>
<div
class="form-group"
:class="{ 'active': active }"
>
<label :for="inputID"><span>{{ inputLabel }}</span></label>
<slot
name="input"
:focusFunc="makeActive"
:blurFunc="makeInactive"
:inputLabel="inputLabel"
:inputID="inputID"
/>
</div>
</template>
<script>
export default {
name: 'TestInput',
props: {
inputLabel: {
type: String,
default: '',
},
inputID: {
type: String,
required: true,
},
},
// data could be put into a mixin
data() {
return {
active: false,
};
},
// methods could be put into a mixin
methods: {
makeActive() {
this.active = true;
},
makeInactive() {
this.active = false;
},
},
};
</script>

用法:

<test-input
:input-i-d="'input-2'"
:input-label="'Description'"
>
<template v-slot:input="scopeVars">
<!-- this is a bootstrap vue input component -->
<b-form-input
:id="scopeVars.inputID"
:state="false"
:placeholder="scopeVars.inputLabel"
@blur="scopeVars.blurFunc"
@focus="scopeVars.focusFunc"
/>
</template>
</test-input>

我想我只是错过了一些东西,或者这是一个 Vue 无法优雅解决的问题?

编辑:如果您正在寻找气泡事件的方法,请开始。但是,我认为这不适用于插槽,这对于解决我的组件问题是必要的。

对于那些想知道这里有两种解决方案的人。似乎我确实对插槽和所有内容都过度思考了这个问题。最初,我觉得为给定元素构建一个基于给定子元素焦点接收类的组件有点太多了。事实证明确实如此,您可以在模板或 css 中轻松解决此问题。

  1. CSS:感谢 @Davide Castellini 提出 :focus-inside 伪选择器。我以前没听说过那个。它适用于较新的浏览器,并且具有可用的填充码。
  2. 模板我写了一个小的自定义指令,可以应用于子元素并处理所有内容。

用法:

v-toggle-parent-class="{ selector: '.placeholder-label', className: 'active' }"

命令:

directives: {
toggleParentClass: {
inserted(el, { value }) {
const parent = el.closest(value.selector);
if (parent !== null) {
el.addEventListener('focus', () => {
parent.classList.add(value.className);
});
el.addEventListener('blur', () => {
parent.classList.remove(value.className);
});
}
},
},
},

尝试使用$emit

孩子:

<input v-on:keyup="emitToParent" />
-------
methods: {
emitToParent (event) {
this.$emit('childToParent', this.childMessage)
}
}

父母:

<child v-on:childToParent="onChildClick">
--------
methods: {
// Triggered when `childToParent` event is emitted by the child.
onChildClick (value) {
this.fromChild = value
}
}

使用此模式可以设置用于更改类的属性 希望这有帮助。如果我误解或需要更好地解释,请告诉我!

最新更新