材料
- PHP(7で動作確認)
- Vue3
- axios
- 連動プルダウンにしたいデータ(DBでも連想配列でも)
連動部分を作る
data.php
<?php
$sushies = array(
array(
'sushi_type' => 0,
'sushi_neta' => 'マグロ'
),
array(
'sushi_type' => 0,
'sushi_neta' => 'サーモン'
),
array(
'sushi_type' => 0,
'sushi_neta' => 'しめサバ'
),
array(
'sushi_type' => 1,
'sushi_neta' => 'いくら'
),
array(
'sushi_type' => 1,
'sushi_neta' => 'うに'
),
array(
'sushi_type' => 1,
'sushi_neta' => 'コーンマヨ'
),
array(
'sushi_type' => 2,
'sushi_neta' => 'かっぱ巻き'
),
array(
'sushi_type' => 2,
'sushi_neta' => 'かんぴょう巻き'
)
);
if (isset($_POST['sushi_type']) && is_numeric($_POST['sushi_type'])) {
$sushi_type = (int)$_POST['sushi_type'];
if ($sushi_type >= 0 || $sushi_type < 3) {
// 検索結果のインデックスが入った配列が返ってる
$indexes = array_keys(array_column($sushies, 'sushi_type'), $sushi_type);
}
}
if (isset($indexes) && !empty($indexes)) {
foreach ($indexes as $index) {
$return_sushies[] = $sushies[$index]['sushi_neta'];
}
} else {
$return_sushies = array_column($sushies, 'sushi_neta');
}
echo json_encode($return_sushies);
ひな形をつくる
index.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/vue@3.2.37"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<title>Linked Pulldown</title>
</head>
<body>
<?php
/**
* htmlspecialcharsの短縮関数
*
* @param string $text エスケープしたい文字
* @return string エスケープ後の文字列
*/
function h(string $text): string
{
return htmlspecialchars($text, ENT_QUOTES, 'utf-8');
}
$sushi_type_array = array('握り', '軍艦', '巻き寿司');
// あとでVue.jsで使うので各所にIDを振っておく
?>
<div id="sushi_zone">
<select name="sushi_type" id="sushi_type">
<?php foreach ($sushi_type_array as $key => $sushi_type) : ?>
<option value="<?php echo h($key) ?>">
<?php echo h($sushi_type) ?>
</option>
<?php endforeach ?>
</select>
<select name="sushi_neta">
</select>
</div>
</body>
</html>
Vueでプルダウンの値を送る処理を書く
linked_pulldown.js
Vue.createApp({
// divに指定したIDを、ここと最終行のmount()の中にセレクタで書く
el: '#sushi_zone',
data() {
return {
// 初期値設定
selectedSushiType: 0,
netas: "",
}
},
methods: {
// 処理内容。今回は寿司の握り方の値をdata.phpに送って結果を受け取る
selected() {
axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest',
};
const parameter = new URLSearchParams();
// ここの第1引数が$_POST変数のインデックスになります。$_POST['sushi_type']
parameter.append('sushi_type', this.selectedSushiType);
axios.post('data.php', parameter)
// echo json_encodeされたもののうちデータ部分がnetasという変数に入ります
.then(res => this.netas = res.data)
.catch(e => alert(e))
},
}
}).mount('#sushi_zone')
VueとHTMLをつなげる
index.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/vue@3.2.37"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<title>Linked Pulldown</title>
</head>
<body>
<?php
/**
* htmlspecialcharsの短縮関数
*
* @param string $text エスケープしたい文字
* @return string エスケープ後の文字列
*/
function h(string $text): string
{
return htmlspecialchars($text, ENT_QUOTES, 'utf-8');
}
$sushi_type_array = array('握り', '軍艦', '巻き寿司');
?>
<div id="sushi_zone">
<!-- @change→要素が変わったときにselected()の中身を実行する。v-modelの指定忘れに注意 -->
<select name="sushi_type" id="sushi_type" v-model="selectedSushiType" @change="selected">
<?php foreach ($sushi_type_array as $key => $sushi_type) : ?>
<option value="<?php echo h($key) ?>">
<?php echo h($sushi_type) ?>
</option>
<?php endforeach ?>
</select>
<!-- netaがさっきVueで受け取ったデータ -->
<select name="sushi_neta">
<option v-for="(neta, index) in netas" :value="index">
{{ neta }}
</option>
</select>
</div>
<script src="linked_pulldown.js"></script>
</body>
</html>
とりあえず完成!
改善が必要な点
- v-forがあるのにkey属性がない
…Vue3から必須級になったようで。この記事の作成過程で知りました。indexをkeyにしてもこのindexは取得してきた値たちに対して連番振るやつなので、データベースから持ってくるのであれば主キーも引っ張り出してkey属性にあてがう必要がありますねこれ - 初期状態の画面がいまいち(例えば初期状態なら全部の寿司ネタ見れるとか)
…プロトタイプだからいいかなと妥協した部分。気が向いたら直したりするかもしれません
その他事項
上に自分が書いた点以外で、「この書き方はよくない」とか「こうしたらもっと良くなる」とかありましたらコメント等でご指導いただけますと幸いです。
あ、htmlspecialcharsのラッパーは手癖で書いてるものです。こういうところでも書いておかないと肝心なところで忘れそうですので…