何を言ってるかわからないだろう。それでいい。いいタイトルが思い浮かばなかった。Webの経験が浅いのでいい言葉を知らない(言い訳)
何がしたいかというと、Spring-Boot + Thymeleaf
で、固定のサイドビュー + メイン画面 のような作りのWeb画面において、画面を再読み込みせずにメイン画面だけ更新したい、という要件があったとする。
例として、/index.html
が上記の固定のサイドビュー + メイン画面の構成として、サーバサイドで以下のようなマッピングでリスンしている状態の場合、/index
への初期遷移時は/index.html
に対してThymeleaf
が働き問題ないが、そのあと、例えばサイドメニューで別項目を選択し、メイン画面を更新する必要がある場合、リクエストを/index/hoge
で行い、サーバが例えばhoge
のModelAndView
を返却したとすると、サーバは/index/hoge.html
を探しに行ってしまい、リソースが無いので400エラーになる。
@RequestMapping(value = "/index", method = RequestMethod.GET)
public ModelAndView index(ModelAndView mav) {
mav.setViewName("index");
mav.addObject("param", "hoge");
return mav;
}
よしんば/index/hoge.html
を置いたとしても、/index.html
と同じサイドビューと同じコードを書くのは流石に無いし、画面がサイドビューごと更新されてしまう。(サイドメニューがアコーディオン表現で開いたり閉じたりするようなメニューにしていた場合、開いていたのに閉じるなど、かなりかっこ悪い動きになる)
仕方ないので、jQuery
の$.ajax()
でJSONデータを取得し、DOMを動的にいじる等で対応していたのだが、せっかくThymeleaf
を使っているのに初期遷移時だけしか使えないというのは何とも寂しい。
色々模索していたところ、とりあえず以下の方法で落ち着いた。
<!-- メインメニュー(JQueryのload()で流し込む) -->
<!-- ① -->
<div id="main_menu"></div>
<script>
// 〜〜(省略)〜〜
// 画面1を読み込む
if (/* 画面1を読み込むための条件判定 */) {
// 画面1をロードする
// ②
$('#main_menu').load('/menu1', null, function () {
// なんか処理
});
}
// 画面2を読み込む
else if (/* 画面2を読み込むための条件判定 */) {
// 画面2をロードする
// ②
$('#main_menu').load('/menu2', null, function () {
// なんか処理
});
}
// 〜〜(省略)〜〜
</script>
// ③
@RequestMapping(value = "/menu1", method = RequestMethod.GET)
public ModelAndView index(ModelAndView mav) {
mav.setViewName("menu1");
mav.addObject("param", "hoge");
return mav;
}
①index.html
に、空のセレクターを用意する。
②jQuery
のload()
関数に①のセレクターを設定し、サーバにリクエストする
③サーバは通常通りの方法でModelAndView
を返却する
こうするとテンプレートも分けられるし、流し込まれるhtml内でThymeleaf
を使うことも出来る。
注意点として、流し込むmenu1.html
にJavaScriptを書くと、load()
されるたびに読み込まれてしまい、関数が同時に何回も呼ばれてしまう。
とりあえず関数はテンプレート単位にJSファイルに分けて、
<script>
$(document).ready(function(){
$.getScript("/js/menu1.js");
$.getScript("/js/menu2.js");
});
</script>
とやったら回避出来た。あと、<head></head>
内は設定しないと文字化け等してしまったので、
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
等は書いた。