見出し画像

【Nuxt.js】input type="checkbox"をコンポーネント化する時のパターン

🎈 WPでも公開中です
https://wp.me/pc9NHC-1l0

#vue #nuxt #プログラミング #エンジニア

前置き

input type="checkbox"
チェックボタン部分のみを
コンポーネントにする時のコードを
書いています✍

シュガーシンタックス
v️-modelのおさらいから
実際のコンポーネントの
サンプルコードなどを
載せていきます🍀

参考:
Checkbox の Vue コンポーネントを作成する Tips
【Vue.js 再入門】 v-model を正しく理解して親子間コンポーネントのデータ伝播をマスターする
【Vue】Input内の値をv-modelのように親子間でバインドさせる方法【サンプルあり】
【Nuxt.js】ネストしたコンポーネントでv-modelの変更を親に反映させる
【Nuxt.js】フォームをコンポーネント化した時のメモ(子Componentで変更されたv-modelの値を親に反映する)


おさらい

v-modelはシュガーシンタックス
v-bind:value, v-on:input
これらを簡単に書けるものでした🌟

<template>
 <input
  type="text"
  :value="value"
  @input="$emit('input', $event.target.value)"
 >
</template>

<script lang="ts">
export default {
 props: {
  value: {
    type: String,
    default: 'ハロー'
  }
 },
}
</script>

この例はtype="text"ですが、
それぞれのtype
入力タグに合わせた
属性のバインディング
そしてイベントにしてくれます💡

・type="input", textareaタグは:valueと@input
・type="checkbox", type="radio"は:checkedと@change
・selectタグは:valueと@change

今回はチェックボックスなので
:checked@changeですね💡


パターン1: 定番

まずは定番の型。
おさらいでやった
type="text"を
type="checkbox"
バージョンにしただけです。

@inputでも動きますが、
意味的にも@changeが良いですね🍀

<template>
 <label class="label">
   <input
     type="checkbox"
     :checked="checked"
     @change="$emit('input', $event.target.checked)"
   />
 </label>
</template>

<script>
export default {
 props: {
   checked: {
     type: Boolean,
     default: false,
   },
 },
}
</script>
<template>
 <div class="page">
   <InputCheckbox v-model="isChecked" />
   <p>{{ isChecked }}</p>
 </div>
</template>

<script>
export default {
 data() {
   return {
     isChecked: '',
   }
 },
}
</script>

##デザインカスタムをするなら…

inputのデザインは変えられないため、
inputを見えないようにし、
spanで装飾をする必要がありますね💫

それを踏まえるとこんな感じ💡

<template>
 <label class="label">
   <input
     class="input"
     type="checkbox"
     :checked="checked"
     @input="$emit('input', $event.target.checked)"
   />
   <span class="mark"></span>
   <span class="text">{{ text }}</span>
   </label>
 </template>
</template>

<script>
export default {
 props: {
   checked: {
     type: Boolean,
     default: false,
   },
   text: {
     type: String,
     default: '',
   },
 },
}
</script>

<style lang="scss" scoped>
.label {
 padding: 12px 8px;
 display: flex;
 align-items: center;
 cursor: pointer;

 &:hover > .mark {
   background: #dddddd !important;
   border: solid 2px #008eff;
 }

 .input {
   margin: 0;
   width: 0;
   opacity: 0;
 }

 .input:focus + .mark {
   background: #dddddd !important;
   border: solid 2px #008eff;
 }

 .input:checked + .mark {
   border: solid 2px #008eff;
   background: #ffffff;
 }

 .input:checked + .mark::before {
   content: '';
   display: block;
   position: absolute;
   top: 50%;
   left: 50%;
   transform: translate(-50%, -45%);
   width: 80%;
   height: 80%;
   background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="%23008EFF" d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/></svg>')
     no-repeat center;
   background-size: contain;
 }

 .mark {
   position: relative;
   top: 0;
   left: 0;
   display: block;
   width: 32px;
   height: 32px;
   border: solid 2px #93cfff;
   background: #ffffff;
   border-radius: 4px;
 }

 .text {
   margin-left: 12px;
   display: block;
   font-size: 18px;
   font-weight: bold;
 }
}
</style>
<template>
 <div class="page">
   <InputCheckbox v-model="isChecked" text="同意する" />
   {{ isChecked }}
 </div>
</template>

<script>
export default {
 data() {
   return {
     isChecked: '',
   }
 },
}
</script>


パターン2: 子でv-model

親でv-modelはあっても
子でv-modelって
全然見たことないですよね?👀

子でチェックのON/OFFを
まとめて管理できるので、
使えると良いな〜😀💭

と思っていたら、
コードを書いてる記事を発見✨🔍
こんな感じです🍀

<template>
 <label>
   <input type="checkbox" v-model="inputedValue" />
   {{ checked }}
 </label>
</template>

<script>
export default {
 model: {
   prop: 'checked',
   event: 'change',
 },
 props: {
   checked: {
     type: Boolean,
   },
 },
 computed: {
   inputedValue: {
     get() {
       return this.checked
     },
     set(newValue) {
       this.$emit('change', newValue)
     },
   },
 },
}
</script>
<template>
 <div class="page">
   <InputCheckbox v-model="checked" />
 </div>
</template>

<script>
export default {
 data() {
   return {
     checked: '',
   }
 },
}
</script>

なんと親でも子でもv-model❗️笑

どういうこと❓と思っていたら
scriptに見慣れない
modelというものがありました。
Vueの公式を探して発見🔍
#model

こんな使い方もあるんですね…✨👏

参考:
【Nuxt.js】ネストしたコンポーネントでv-modelの変更を親に反映させる
【Vue.js 再入門】 v-model を正しく理解して親子間コンポーネントのデータ伝播をマスターする
【Nuxt.js】フォームをコンポーネント化した時のメモ(子Componentで変更されたv-modelの値を親に反映する)

そういえばVue3の変更点は
なんだったけな〜と思っていたら、
同じ箇所で複数使用できる、でした。
やりたいこととは違いますが、
備忘録的に残しておきます🌟
Vue3でv-modelがどう変わったか

##これはNG

パッと思いついて
やりがちなのは、
v-modelの値をpropsで
親から渡す…
こんな感じではないでしょうか。

<template>
 <label>
   <input type="checkbox" v-model="checked" />
 </label>
</template>

<script>
export default {
 props: ['checked'],
}
</script>
<template>
 <div class="page">
   <InputCheckbox checked="true" />
 </div>
</template>

しかしこれだと
「読み書き」の「読み」
しかできないんです。

実際、チェックを触らなければ
エラーはないのですが、
つけ外しを行うとエラーになります💥✋


まとめ

form要素のコンポーネントを
パターン化しておきたいな〜
と思って今回はcheckboxに✍

type="text"以外は
結構ややこしくて
苦戦しています😵💦笑

でもこれが理解できたら
すごく便利だし、
form要素ってinputのtypeも
他のタグも入れたら結構あるし、
コンポーネントにして
スッキリ分けたいじゃないですか…🧹

なので各要素の記事と、
各要素の良いコードを抜粋して
全部を一覧で見れる記事を
書こうと思っています…🎈🧸


🎈 WPでも公開中です
https://wp.me/pc9NHC-1l0

この記事が気に入ったらサポートをしてみませんか?