vue.jsでTypeScriptのeventをHandleしてそれを親コンポーネントに渡したいというときに型を何にすればよいか困ったので、その解決策。 以下2つのhtmlとtypescriptを使うことで解決。
やりたかったこと
環境
vue: 2.5.11 typescript: 2.9.2
ハマったこと
- eventの型がわからなかった。
- inputのtextが1文字入れるだけでfocusが外れる。
対応
eventの型
以下の場合、引数の型は正しいのだが、typescriptのcompileが通らない。
updateInput(event: Event): void { const text = event.target.value; this.$emit(inputEvent, text); },
なぜかというと、Event型はInterfaceなので、 event.target
にvalueがないためだ。
ではどうしてやるかというと、 instance of
で判定してあげる必要がある。
if (event.target instanceof HTMLInputElement) { this.$emit(inputEvent, tevent.target.value); }
focusの対応
html内のinputイベントは文字を1文字入力するごとに発火する。 mouseoverイベントなどもmouseを乗せるたびに発火する。
inputイベントだけ実装していれば問題ないのだが、mouseイベントも同じinput属性に実装しているかつクラスなどのDOM構成を変えるような実装をしていると、以下の問題が生じる
- input textをマウスで選択 (mouseoverイベント発火)
- input textに1文字入力する (input, mouseoverイベント発火)
- mouseをずらす (mouseoutイベント発火)
このため、なにかinputで文字を入力するたびにmouseoverイベントが発火し、DOMが変更が検知されDOMを作り変えてしまうため、inputからfocusが勝手にはずれてしまう。
対処方法としては、inputで文字を送るのではなく、focusoutとmouseoutで親コンポーネントにemitする。
全体の実装
<template> <input class="input-text" type="text" :value="text" :class="{'input-hover': isHover}" @mouseover="mouseover" @mouseout="mouseout" @focusout="updateInput" /> </template>
<script lang="ts"> import Vue from 'vue'; const inputEvent = 'inputed'; export default Vue.extend({ props: { text: String, }, data() { return { isHover: false, }; }, methods: { updateInput(event: Event): void { event.preventDefault(); if (event.target instanceof HTMLInputElement) { const text = event.target.value; this.$emit(inputEvent, text); } }, mouseover(event: Event): void { event.preventDefault(); this.isHover = true; }, mouseout(event: Event): void { event.preventDefault(); this.isHover = false; if (event.target instanceof HTMLInputElement) { const text = event.target.value; this.$emit(inputEvent, text); } }, }, }); </script>