GameWith Developer Blog

GameWith のエンジニア、デザイナーが技術について日々発信していきます。

Vue テンプレート上のイベントハンドラ引数からオブジェクト参照を渡す方法を考えました #GameWith #TechWith #vue #vuejs

こんにちは! @53ble です。

今回は、<template> 上からリアクティブプロパティの特定要素を更新させる UI が多数あった場合に、イベントハンドラ定義を減らす方法について話していきたいと思います。

概要

SFCの <template> ブロック内に記述されているイベントハンドラ引数からオブジェクト参照を渡すことが可能になります。

UI

本エントリーは、パワプロ攻略|パワプロアプリ最速攻略 みんなの選手査定ランキング 投稿画面の実装を例に説明します。

xn--odkm0eg.gamewith.jp

次の手順で選択されたキャラクターが保持されているようにリアクティブな input 要素を実装します。

1. メインキャラクター選択を行うため対象の要素選択

2. キャラクターを選択

3. キャラクターが選択された後

実装

今回は、パスをクエリ文字列で指定してオブジェクトを操作するライブラリの wild-wild-path を利用してイベントハンドラをスマートに実装します。

github.com

wild-wild-pathset メソッドについて説明します。

const target = { colors: ['red', 'blue'] }
set(target, 'colors.0', 'yellow') // ['yellow', 'blue']

第2引数のクエリ文字列でオブジェクトプロパティのパスと配列の添字を指定し、第3引数の値を代入します。

set() の戻り値は、ディープクローンです。

⚠️: 以下、掲載してあるコードは本エントリーのために整形してあります。

テンプレート

フォーム部品

複数の input 要素に関連付けらた label 要素が v-for で連続しています。

input 要素の value には選択されるキャラクタのIDが v-model でバインディングされるようになっています。

<label
  v-for="(id, index) of state.targetCharacterIds"
  :key="index"
  type="button"
  @click="
    onOpenModal(optionsCharacter, `targetCharacterIds.${index}`)
  "
>
  <input
    v-model="state.targetCharacterIds[index]"
    type="number"
  />
</label>

モーダルウィンドウ

キャラが選択されると、selected イベントが発火し、キャラクターの ID がペイロードで渡されます。

WalkthroughBuildCardSelect コンポーネントの内部実装は割愛させていただきます。

<WalkthroughBuildCardSelect
  :opts="state.options"
  @selected="onSelected"
/>

リアクティブプロパティ

targetCharacterIds は、配列要素がそれぞれ複数の input 要素の value に v-model でバインドされていて、キャラクターの ID が代入されます。

selectedTarget は、キャラクターの ID を代入する参照を文字列として保持します。

options は、キャラ選択の項目データが代入されます。

  const state = reactive<State>({
    targetCharacterIds: [NaN, NaN, NaN, NaN, NaN, NaN],
    selectedTarget: "",
    options: [],
  });

イベントハンドラ

テンプレート上のイベントハンドラ引数から、キャラクターの ID を代入する参照先が state.selectedTarget に渡ります。

const onOpenModal = (
  options: Options,
  target: string
): void => {
  state.options = options;
  state.selectedTarget = target;
};

モーダルウィンドウの WalkthroughBuildCardSelect コンポーネントから、キャラクターの ID が渡されると state.selectedTarget で保持されている値から参照先を指定して値を書き換えます。

const onSelected = (data: number): void => {
  const _state = set(state, state.selectedTarget, data);
  Object.assign(state, _state); // 書き換え後のディープクローンされたオブジェクトで state を上書きする
};

まとめ

今回実装した画面では、モーダルウィンドウで選択したキャラクターの ID を反映させる input 要素は、投稿フォーム中にたくさんある仕様でした。

モーダルウィンドウがどの input 要素に対してキャラクターの ID を選択するために開いているのかを動的することでonSelected の定義でイベントハンドラをひとつに集約させることができました。

wild-wild-path の メソッドはワイルドカードや正規表現にも対応しているので、テンプレートから参照を指定する表現の幅はたくさんあるように思いました。