Vue.jsで再利用性の高いボタンやインプットタグを作成する方法

Vue.js

Vue.jsで再利用性の高いボタンやインプットタグを作成する方法を紹介します。

Vue.jsで再利用性の高いボタンを作成する方法

componentsフォルダに、次のAppButton.vueを作成します。

AppButton.vueを作成

<template>
  <button
    class="button"
    :class="btnStyle"
    v-bind="$attrs"
    v-on="$listeners"><slot /></button>
</template>

まず、templateで、$attrsを動的にします。
そして、Vue v2系の場合は、$listenersをv-onする必要があります。
Vue v3系の場合は、$attrsに$listenersが内在されているので不要になります。
関連の公式サイト記事:$listeners removed

そして、buttonクラスといったすべてのボタンに共通するCSSは静的なクラスとして定義して、それぞれのボタンで変更するCSSは、動的なクラスを定義しておくことで、柔軟なカスタマイズが可能になります。

さらに、ボタンの文字部分は、slotで渡すことで自由な文字列を表示することができます。

<script>
export default {
  name: 'AppButton',
  props: {
    btnStyle: {
      type: String,
      default: ''
    }
  }
}</script>

scriptでは型を指定しておきます。

あとは、スタイル部分で使うボタンのCSSを記述します。

<style scoped>
.button {
  font: inherit;
  background-color: red;
  color: white;
  padding: 5px;
  cursor: pointer;
  border: 1px solid red;
}

.button:hover,
.button:active {
  background-color: rgb(51, 51, 51);
  border: 1px solid red;
}

.reverse {
  background-color: white;
  color: black;
  border: 1px solid transparent;
}

.reverse:hover,
.reverse:active {
  color: #ccc;
  background-color: white;
  border: 1px solid transparent;
}

.cancel {
  background-color: red;
  color: white;
  border: 1px solid transparent;
}

.cancel:hover,
.cancel:active {
  background-color: salmon;
  border: 1px solid transparent;
  color: white;
}</style>

上の場合、cancelやreverseクラスを付け替えるだけで、異なるスタイルのボタンが作成されます。

AppButton.vueを使う

buttonクラスのみを適用する

<AppButton @click="$router.push('/admin/new-post')">記事を新規作成</AppButton>

クラスは特に指定していないので、buttonクラスのみが適用されます。
「記事を新規作成」という文字列がslotによって出力されます。
クリックイベントを設定して、飛ばしたいページに飛ぶボタンが作成できます。

動的なクラスを適用する

        <AppButton type="submit">{{ isLogin ? 'ログイン' : 'サインアップ' }}</AppButton>
        <AppButton
          type="button"
          btn-style="reverse"
          style="margin-left: 15px"
          @click="isLogin = !isLogin">{{ isLogin ? 'サインアップ' : 'ログイン' }}へ切り替える</AppButton>

<AppButton type="submit">保存</AppButton>
            
    <AppButton
              type="button"
              style="margin-left: 10px"
              btn-style="cancel"
              @click="onCancel">キャンセル</AppButton>

$attrsをv-bindしていることで、上の2つのようにtype属性が異なっていても一つのコンポーネントで対応できます。
動的なクラスbtnStyleに渡すため、btn-styleとして適用したいクラスをそれぞれ指定することができます。
reverseクラスでなく、cancelとすればcancelクラスが適用できます。
また、補助的なスタイルを付け足したい場合などは、style属性で別途追加することも可能です。

Vue.jsで再利用性の高いインプットタグを作成する方法

AppControlInput.vueを作成します。

AppControlInput.vueを作成

<template>
  <div class="input-control">
    <!-- ラベル -->
    <label><slot /></label>
    <!-- インプット -->
    <input
      v-if="controlType === 'input'"
      v-bind="$attrs"
      :value="value"
      @input="$emit('input', $event.target.value)">
      <!-- テキストエリア -->
    <textarea
      v-if="controlType === 'textarea'"
      rows="10"
      :value="value"
      @input="$emit('input', $event.target.value)"></textarea>
  </div>
</template>

labelでは、slotを使うことで、柔軟に出力内容を変更できるようにします。

inputタグとtextareaタグをv-ifで切り替えることで一つのコンポーネントで対応できます。
inputタグの場合、type属性がよく変わるので、$attrsをv-bindします。

<script>

export default {
  name: 'AppInputControl',
  props: {
    controlType: {
      type: String,
      default: 'input'
    },
    value: {
      type: String,
      default: ''
    }
  }
}</script>
<style scoped>
.input-control {
  margin: 10px 0;
}

.input-control label {
  display: block;
  font-weight: bold;
}

.input-control input,
.input-control textarea {
  display: block;
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  border: 1px solid #ccc;
  padding: 5px;
}

.input-control input:focus,
.input-control textarea:focus {
  background-color: #eee;
  outline: none;
}</style>

AppControlInput.vueを使う

<form>
 <AppControlInput type="email">Eメール</AppControlInput>
 <AppControlInput type="password">パスワード</AppControlInput>
</form>
<AppControlInput v-model="author">投稿者</AppControlInput>

<AppControlInput v-model="title">タイトル</AppControlInput>
        
<AppControlInput v-model="thumbnailLink">サムネイル</AppControlInput>


上のようにすることで、$attrsをv-bindしているので、異なるtype属性を動的に切り替えることができます。
上のケースではscriptでデフォルトをinputにしているので、特に指定しなければinputタグが出力されます。

<AppControlInput
              control-type="textarea"
              v-model="content">コンテンツ</AppControlInput>

textareaタグにしたい場合、v-ifで設定したcontrolTypeを指定すればいいので、control-typeにtextareaを指定します。

Vue.js

Posted by devsakaso