从父组件访问子组件的子元素

huangapple go评论69阅读模式
英文:

Access child element of a component from a parent component

问题

我有一个Form组件,它渲染了3个InputContainer组件。我想在表单的submit事件中访问input元素的值。目前,我的submitResource函数记录了包含labelinput元素的div

如果不是必要的话,我不想使用@change处理程序发出任何事件。我唯一成功访问input元素的方式是通过console.log(titleRef.value.$el.querySelector('input'));,但我认为这不是正确的方式。

谢谢!

Form.vue

<template>
  <form @submit.prevent="submitResource">
    <InputContainer ref="titleRef" />
    <InputContainer ref="descriptionRef"/>
    <InputContainer ref="linkRef" />
    <BaseButton type="submit" class="submit">Add Resource</BaseButton>
  </form>
</template>

<script setup>
import BaseButton from './BaseButton.vue';
import InputContainer from './InputContainer.vue';
import { ref } from 'vue';

const titleRef = ref(null);
const descriptionRef = ref(null);
const linkRef = ref(null);

const submitResource = () => {
  console.log(titleRef.value.$el);
};
</script>

InputContainer.vue

<template>
  <div class="input-container">
    <label for="example">Example</label>
    <input
      type="text"
      id="example"
      name="example"
    />
  </div>
</template>
英文:

I have a Form component which renders 3 InputContainer components. I want to access the value of the input element on the submit event of the form. Right now, my submitResource function logs the div which contains both label and input element.

If not necessary, I don't want to emit any events with @change handler. The only way I've managed to access the input element is by console.log(titleRef.value.$el.querySelector(&#39;input&#39;)); but I think its not the right way to do it.

Thank you!

Form.vue

&lt;template&gt;
  &lt;form @submit.prevent=&quot;submitResource&quot;&gt;
    &lt;InputContainer ref=&quot;titleRef&quot; /&gt;
    &lt;InputContainer ref=&quot;descriptionRef&quot;/&gt;
    &lt;InputContainer ref=&quot;linkRef&quot; /&gt;
    &lt;BaseButton type=&quot;submit&quot; class=&quot;submit&quot;&gt;Add Resource&lt;/BaseButton&gt;
  &lt;/form&gt;
&lt;/template&gt;

&lt;script setup&gt;
import BaseButton from &#39;./BaseButton.vue&#39;;
import InputContainer from &#39;./InputContainer.vue&#39;;
import { ref } from &#39;vue&#39;;

const titleRef = ref(null);
const descriptionRef = ref(null);
const linkRef = ref(null);

const submitResource = () =&gt; {
  console.log(titleRef.value.$el);
};
&lt;/script&gt;

InputContainer.vue

&lt;template&gt;
  &lt;div class=&quot;input-container&quot;&gt;
    &lt;label for=&quot;example&quot;&gt;Example&lt;/label&gt;
    &lt;input
      type=&quot;text&quot;
      id=&quot;example&quot;
      name=&quot;example&quot;
    /&gt;
  &lt;/div&gt;
&lt;/template&gt;

答案1

得分: 1

你可以使用 defineExpose 来将子组件的 ref 转发出去。

InputContainer.vue:

<template>
  <div class="input-container">
    <label for="example">Example</label>
    <input
      type="text"
      name="example"
      ref="inputRef"
    />
  </div>
</template>
<script setup>
import { ref } from 'vue';
const inputRef = ref(null);
defineExpose({
    inputRef
});
</script>

然后,在 Form.vue 中,你可以这样访问它:

const submitResource = () => {
  const titleInputEl = titleRef.value.inputRef;
};
英文:

You can use defineExpose to forward a ref from the child.

InputContainer.vue:

&lt;template&gt;
  &lt;div class=&quot;input-container&quot;&gt;
    &lt;label for=&quot;example&quot;&gt;Example&lt;/label&gt;
    &lt;input
      type=&quot;text&quot;
      name=&quot;example&quot;
      ref=&quot;inputRef&quot;
    /&gt;
  &lt;/div&gt;
&lt;/template&gt;
&lt;script setup&gt;
import { ref } from &#39;vue&#39;;
const inputRef = ref(null);
defineExpose({
    inputRef
});
&lt;/script&gt;

Then, you can access it in Form.vue.

const submitResource = () =&gt; {
  const titleInputEl = titleRef.value.inputRef;
};

答案2

得分: 1

如果有帮助的话,在Vue中完成这个任务的典型方式是通过v-model进行双向绑定。为了使其工作,您需要构建您的输入组件以通过:modelValue属性接收值,并使它们触发@update:modelValue事件:

Form.vue

<template>
  <form @submit.prevent="submitResource">
    <InputContainer v-model="formData.title" />
    <InputContainer v-model="formData.description"/>
    <InputContainer v-model="formData.link" />
    <BaseButton type="submit" class="submit">Add Resource</BaseButton>
  </form>
</template>

<script setup>
import BaseButton from './BaseButton.vue';
import InputContainer from './InputContainer.vue';
import { ref } from 'vue';

const formData = ref({
  title: '',
  description: '',
  link: '',
});

const submitResource = () => {
  console.log(formData.value);
};
</script>

InputContainer.vue

<template>
  <div class="input-container">
    <label for="example">Example</label>
    <input
      type="text"
      id="example"
      :value="modelValue"
      @input="emit('update:modelValue', $event.target.value)"
    />
  </div>
</template>

这种方法有几个优点,其中包括:

  • 松耦合:父组件不需要了解子组件的内部结构
  • 单一数据引用:所有数据都在数据层中表示,并且可靠地保持最新状态(而不是在模板中漂浮)- 这允许进行高效的验证、调试等。
  • 可读性:绑定在模板中是显而易见的,而不是深藏在代码中的某个地方。
  • 可移植性:几乎所有Vue组件都使用这种格式,您可以轻松地用您的输入组件替换其他组件,反之亦然。
  • 无意外:如果其他开发人员与您的代码一起工作,他们会期望这种方式。
英文:

If it helps, the typical way to do this in Vue is through two-way binding with v-model. For this to work, you build your input components to receive a value through the :modelValue prop and have them emit an @update:modelValue event:

Form.vue

&lt;template&gt;
  &lt;form @submit.prevent=&quot;submitResource&quot;&gt;
    &lt;InputContainer v-model=&quot;formData.title&quot; /&gt;
    &lt;InputContainer v-model=&quot;formData.description&quot;/&gt;
    &lt;InputContainer v-model=&quot;formData.link&quot; /&gt;
    &lt;BaseButton type=&quot;submit&quot; class=&quot;submit&quot;&gt;Add Resource&lt;/BaseButton&gt;
  &lt;/form&gt;
&lt;/template&gt;

&lt;script setup&gt;
import BaseButton from &#39;./BaseButton.vue&#39;;
import InputContainer from &#39;./InputContainer.vue&#39;;
import { ref } from &#39;vue&#39;;

const formData = ref({
  title: &#39;&#39;,
  description: &#39;&#39;,
  link: &#39;&#39;,
});

const submitResource = () =&gt; {
  console.log(formData.value);
};
&lt;/script&gt;

InputContainer.vue

&lt;template&gt;
  &lt;div class=&quot;input-container&quot;&gt;
    &lt;label for=&quot;example&quot;&gt;Example&lt;/label&gt;
    &lt;input
      type=&quot;text&quot;
      id=&quot;example&quot;
      :value=&quot;modelValue&quot;
      @input=&quot;emit(&#39;update:modelValue&#39;, $event.target.value)&quot;
    /&gt;
  &lt;/div&gt;
&lt;/template&gt;

This approach has several benefits, among them is:

  • loose coupling: parent does not need to know about internal structure of child
  • single source of reference: all data is represented in the data layer and reliably up to date (instead of floating around in the template) - this allows for efficient validation, debugging, etc.
  • readability: binding is apparent in the template instead of buried somewhere in the code
  • portability: pretty much all Vue components use this format, you can easily replace other input components with yours and vice versa
  • no surprises: if other developers work with your code, this is what they'll expect

huangapple
  • 本文由 发表于 2023年7月17日 20:53:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76704671.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定