상황 🤦♀️
페이지에서 특정 Input 에 focus 를 해주고 싶다. (회원가입 폼에서 완료 버튼 클릭했는데, 필수로 입력해야 하는 이름을 입력하지 않아서 이름 인풋에 포커스해주고 싶을 때 등등) 그런데 이 페이지에는 여러 InputGroup이 있고, 그 안에 Input들이 여러 개 있다.
몇번 InputGroup의 몇번 Input인지는 아는데, 어떻게 그 input에 focus를 해주지??
사실 회사에서 비슷한 작업을 하고 있는데 이 부분을 모르겠다... (회사 코드는 훨씬 복잡하고 몇번 InputGroup의 몇번 Input을 포커스해야 하는지 알아내는 것도 오래 걸렸다..! 아래 내용, 코드는 내가 공부용으로 간단하게 만든 것~~ !! )
설명 ❤️
완성 ~!!
Message 부분에서 몇 번 InputGroup의 몇 번 Input에 포커스 해줘야 하는지 판단한다. (실제 코드에서는 그걸 판단하는 로직도 복잡했는데, 여기서는 간단히 임의의 수로 정했다!
그리고 Message에서 provide로 두 숫자를 넘겨주고, Input에서 inject로 받는다.
InputGroup은 자신이 몇번째 InputGroup인지 알 수 있게 props를 받는다. Input에서는 자신이 몇번째 InputGroup의 몇번째 Input인지 알 수 있게 props를 받는다.
Input은 자신의 위치와 타겟의 위치를 비교해서, 자신이라면 focus를 한다. 일단 이 코드에서는 onMounted에만 한번 포커스하게 했다. 근데 실제에서는 필요한 상황에 포커스를 시켜주면 될 것 같다 ~
코드 👩💻
Message.vue
<template>
<div>
<h1 class="text-center">메시지</h1>
<section class="text-center">
<InputGroup :index="0" :attrs="inputAttrs" ref="inputsRef1" />
<hr />
<InputGroup :index="1" :attrs="inputAttrs2" ref="inputsRef2" />
<li>
<input type="text" ref="input" />
</li>
</section>
</div>
</template>
<script lang="ts" setup>
import { ref, provide } from "vue";
import InputGroup from "@/components/InputGroup.vue";
const inputAttrs = [
{
label: "nickname",
type: "text",
placeholder: "nickname을 입력하세요",
value: "",
index: 0,
},
{
label: "message",
type: "text",
placeholder: "message을 입력하세요",
value: "",
index: 1,
},
];
const inputAttrs2 = [
{
label: "email",
type: "text",
placeholder: "email을 입력하세요",
value: "",
index: 0,
},
{
label: "mbti",
type: "text",
placeholder: "mbti를 입력하세요",
value: "",
index: 1,
},
];
const targetGroupIndex = ref<number>(0);
const targetItemIndex = ref<number>(1);
provide("targetGroupIndex", targetGroupIndex.value);
provide("targetItemIndex", targetItemIndex.value);
</script>
<style></style>
InputGroup.vue
<template>
<div>
<ul>
<li v-for="(item, idx) in props.attrs" :key="idx" ref="inputsRef">
<Input
:groupIndex="props.index"
:itemIndex="idx"
:attr="item"
@setValue="setValue"
/>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, computed, watchEffect } from "vue";
import Input from "./Input.vue";
const props = defineProps<{
index?: number;
attrs: Array<{
label?: string;
type: string;
placeholder?: string;
value?: string;
}>;
}>();
const setValue = (content: string) => {
console.log(content);
};
</script>
Input.vue
<template>
<input
:name="props.attr.label"
:type="props.attr.type"
:placeholder="props.attr.placeholder"
:value="props.attr.value"
@input="(event) => setValue(event)"
ref="inputEl"
/>
</template>
<script setup lang="ts">
import { ref, inject, onMounted, watch } from "vue";
const emit = defineEmits(["setValue"]);
const props = defineProps<{
groupIndex?: number;
itemIndex?: number;
attr: {
label?: string;
type: string;
placeholder?: string;
value?: string;
index?: number;
};
}>();
const inputValue = ref<string>("");
const inputEl = ref<HTMLInputElement | null>(null);
const setValue = (event: InputEvent | Event): void => {
const target: HTMLInputElement = event.target as HTMLInputElement;
inputValue.value = target.value;
emit("setValue", inputValue.value);
};
const targetGroupIndex = inject("targetGroupIndex");
const targetItemIndex = inject("targetItemIndex");
const focusOnTargetInputEl = () => {
if (
targetGroupIndex === props.groupIndex &&
targetItemIndex === props.itemIndex
) {
inputEl.value?.focus();
}
};
onMounted(() => {
focusOnTargetInputEl();
});
</script>
<style lang="scss" scoped>
input {
border: 1px solid black;
border-radius: 4px;
padding: 4px;
margin: 4px;
}
</style>
이 코드가 포함된 내 레포 ~!
https://github.com/tjdls111/birthday_vue/commit/d2e917c15201090545f4316a7edb429371fd376f
참고 자료 😍
=> 이걸 보면 Vue에서 어떻게 focus 를 하면 될지에 도움이 된다. focus를 해줄 때 이해해야 하는 가상 돔, ref 등을 알려준다.
- vue에서 focus를 하는 등 돔 요소를 조작할 때 refs를 사용한다.(바닐라 JS에서 getElementById 하듯이)
- 컴포넌트에서 refs를 사용하고 싶으면, 속성에 refs를 추가한다. 구별할 수 있는 값을 줘야 한다. (ref="구별할 수 있는 이름")
아래 자료는 ref 를 이해하는 데 도움이 된다.
https://vuejs.org/guide/essentials/template-refs.html
타입 스크립트 + composition api 는 이거 보기 ! !
https://vuejs.org/guide/typescript/composition-api.html#typing-template-refs
후..ㅎㅎ 이렇게 긴 블로그 글은 처음 써보는 것 같네요! 읽어주셔서 감사합니다~ 혹시 궁금한 부분이나 오류가 있으면 알려주세요 :)
'프론트엔드💛' 카테고리의 다른 글
[CSS] 글자가 영역 넘어갔을 때 ... 으로 보이게 하기 (0) | 2022.07.28 |
---|---|
[vue] can't resolve 'sass-loader' 에러 (0) | 2022.07.18 |
[Vue] component name should always be multi-word 에러 (0) | 2022.07.18 |
[Vue3] emit 쓸 때 파라미터 여러 개 보내기 (0) | 2022.07.13 |
[Vue3] provide와 inject / inject name is not a function 에러 (미완) (0) | 2022.07.13 |