前回までのあらすじ
Vueでフロント楽勝!
前回は、軽くNuxt(Vue)について解説しました。簡単に電卓を作ってVueの仕組みについて解説しました。 なので今回はちょっと応用して、1ページ完結の"簡単なWebアプリ"を作っていきましょう!
【なに作るん?】お問合せフォームを作ろう!
どんなもの?
今回作るもの1つ目は、「お問合せフォーム」です。 イメージしやすいのは、Googleフォームですかね。一応今回は、入力(文字とか)、選択(チェックボックスとか)にしましょうか。 ※今回も別記事より解説したPugとBulmaを使用しています。
まずは完成ソースを見てみよう!
<template lang="pug">
.container
form.box(@submit.prevent="sendForm")
.field
label.label.title.is-flex お名前<span style="color: red">*</span>
.control
input.input(type="text"
maxlength="10"
placeholder="お名前"
v-model="formData.name")
.field
label.label.title.is-flex メールアドレス<span style="color: red">*</span>
.control
input.input(
type="email"
placeholder="examlple@airt.com"
size="30"
v-model="formData.email")
.field
label.label.title.is-flex パスワード<span style="color: red">*</span>
.control
input.input(
type="password"
placeholder="password"
maxlength="50"
v-model="formData.password")
.field
.content-row-center
label.label.title.is-flex 性別<span style="color: red">*</span>
.control
.select
select(v-model="formData.sex")
option(v-for="sex in sexList"
:key="sex.title"
:value="sex.value") {{ sex.title }}
label.label.title.is-flex 生年月日<span style="color: red">*</span>
.control
input.input(type="date"
v-model="formData.birth")
.field
label.label.title.is-flex 選択(ラジオボタン)
.control
.checkbox-input-group
.radio-input(v-for="v in selectList")
input.button(
type="radio"
:id="v.value"
:value="v.value"
v-model="formData.radio")
label.subtitle.p-2(:for="v.value") {{ v.title }}
.field(v-if="formData.radio == 1")
label.label.title.is-flex 好きな数字は?
.control
input.input(type="number"
maxlength="10"
placeholder="好きな数字は?"
v-model="formData.favNum")
.field
label.label.title.is-flex 資料
.upload_zone
.control
label.image(for="file_imgs")
.drop_zone(
:class="{'enter': isEnter}")
input.input_file(type="file"
id="file_imgs"
multiple=""
accept="image/*"
ref="preview"
@change="inputImage"
style="display:none;")
p ここをクリック or ファイルをドラッグ&ドロップ
.field
label.label.title.is-flex 範囲
.control
input(
type="range"
id="range"
min="0" max="10"
v-model="formData.range")
label.subtitle.p-2(for="range") {{ formData.range }}
.field
label.label.title.is-flex 自由欄
.control
textarea.textarea(
cols="20"
rows="10"
maxlength="600"
v-model="formData.note")
button.button.is-primary(type="submit") 送信する
</template>
<script setup>
const sexList = [
{ title: "回答しない", value: 0 },
{ title: "女性", value: 1 },
{ title: "男性", value: 2 },
];
const selectList = [
{ title: "選択肢1", value: 1 },
{ title: "選択肢2", value: 2 },
{ title: "選択肢3", value: 3 },
{ title: "選択肢4", value: 4 },
{ title: "選択肢5", value: 5 },
];
const formData = ref({
name: "",
email: "",
password: "",
birth: "1990-01-01",
sex: 0,
radio: 1,
range: 0,
fileList: [],
note: "",
favNum: 0,
});
function inputImage() {
let fileList = [];
var fl = preview.value.files.length;
var i = 0;
while (i < fl) {
// ループ内のファイル var をローカライズ
var file = preview.value.files[i];
fileList.value.push(file);
i++;
}
formData.value.file = fileList;
}
function sendForm() {
alert(`ここで送信できるよ!${formData.value.name}さん`);
}
</script>
<style lang="scss" scoped>
.content-row-center {
display: flex;
align-content: center;
justify-content: center;
align-items: center;
}
.checkbox-input-group {
display: flex;
justify-content: flex-start;
flex-direction: row;
align-items: flex-start;
align-content: flex-start;
flex-wrap: wrap;
// overflow-x: scroll;
}
.drop_zone {
border: 3px solid lightgray;
border-radius: 20px;
// width: 300px;
height: 150px;
display: flex;
justify-content: center;
align-items: center;
color: lightgray;
font-weight: bold;
// margin-top: 20px;
}
.enter {
border: 3px dotted lightblue;
color: lightblue;
}
.radio-input,
.checkbox-input {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
position: relative;
input {
display: none;
padding: 0;
margin: 0;
}
label {
cursor: pointer;
background-color: var(--color-mono-7);
border-radius: 12px;
}
label::before {
content: "□";
}
input:checked ~ label {
color: white;
background-color: #323232;
border-radius: 12px;
}
input:checked ~ label::before {
content: "◆";
}
}
</style>
【解説】v-modelって?
難しい言葉で言うと"双方向バインディング"できるものです。 簡単にいうと、inputとかで変更すると画面に出している値も同時に変更されるものですね。え、「そんなん当たり前じゃないの?」と思う方多いと思いますが、実はちょっと違うんです。 普通にやる時、入力してから処理を走らせる(ボタンを押すなど)でやっと変更されるんですよね。v-modelによって、この「値変えたから更新してねボタン」を丸々省くことができるということです。 とりあえず、inputにv-modelを使っておけば大丈夫、ということですね!笑
【解説】いろんなinputのtype
入力欄で、普通に文字を入れてもらいたいときは type="text"としますが、他にも色々なものを指定することができます。今回使ったものも含めて以下のものがあります。 ・text:一般的なやつ。文字大体入る。基本これ使う。 ・number:数字のみ入力可能。 ・email:メールアドレスっぽいものしか受け付けない(※入力はできるがenter時に判定される) ・password:パスワードとかで使う。入った値は・で隠される。 ・date:日付形式なやつ。データは"1990-01-01"みたいにでてくる。 ・radio:一つのみ選択できるやつ。 ・checkbox:複数選択できるやつ。 ・file:画像とかを入れるやつ。拡張子で制限も可能 ...他にも色々あるので見てみてね!
【解説】v-forやv-ifって?
似たようなものを繰り返しやりたいとき、2,3個ぐらいあればベタ書きでもいいですが、20個30個になるとそうもいきませんよね? 例えば、今回のでいうと"性別"のプルダウンであれば optionの男と女と回答なしの3つを書くだけでいいですが、"選択(ラジオボタン)"が20個あると考えたときに、 inputと labelを20個作るのはダルい。。しかも、「15番目の名称違うから直そ」としてもソースがなっがすぎて見にくいし管理が大変なんですよね。 そんなときにv-forを使いましょう。今回は"選択(ラジオボタン)"に使う配列を用意してv-forするだけ。これだけで20個分が省略できるのです!すごい! あとは、全記事でも紹介したv-ifですね。これは単純に条件に合うときだけ表示させる、というものです。今回で言うと、選択(ラジオボタン)で選択肢1を選んだ時のみ"好きな数字は?"が表示されます。
【解説】submitで送信処理
今回 formで全体を括っているのですが、基本的にこういう入力系はformでくくり、送信などの処理発火ではbuttonの type-"submit"し、action="送信先のURL"としてAPIに渡す、のが普通です 。 ですが僕はあまりこれをせず、一旦関数で処理をしたあとにAAPIに渡す。ということの方が多いです。 ※なので、formを使わずにすることもできます。
なに言ってるのかわからないと思うので簡単にいうと、「とりあえずformでくくって@submit.prevent="送信する関数"つけて、終わりぎわにボタンのbutton(type="submit")をおけばOK」です。 これにより、"送信する関数"が走らせることができます。今回はボタンを押すと「ここで送信できるよ!●●さん」と出ます。 →本番はここをバリデーションチェックをしたり、データベースに送る処理やAPIに渡すなどをする。
まとめ
え、解説これだけなん?と思うかともいます。実際自分も記事書いてて解説短かっって思いました。 逆に言えば、これだけでお問合せフォームが作れるってことです。簡単でしょ? もしよかったら実際にNuxtやってみてください!
