概要
- Thymeleafのfragment属性を使ってモーダルを子要素として切り出す。
- モーダルはBootstrap4を使用して作成する。
完成イメージ
宿泊プラン一覧
↓
「空室確認」を押すとその宿泊プランの空室状況が表示される
環境
Spring Boot
maven
Thymleaf
Postgresql
プロジェクトの構成
sample-qiita
│ pom.xml
└─src
└─main
├─java
│ └─com
│ └─example
│ └─domain
│ │ Plan.java
│ │ Room.java
│ │ Calender.java
│ └─controller
│ │ PlanListController.java
│ └─service
│ │ PlanListService.java
│ │ VacancyRoomService.java
│ └─repository
│ PlanMstRepository.java
│ CalenderRepository.java
└─resources
│ application.yml
├─db
│ └─migration
│ V1__createTable.sql
│ V2__insertData.sql
├─static
│ └─css
│ │ common.css
│ └─img
│ └─rooms
│ room1.jpg
│ room2.jpg
│ room3.jpg
└─templates
plans_list.html
modal_vacancy_room.html
1.サーバー側
pom.xml
pom.xml
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>sample-qiita</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sample-qiita</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
db
V1__createTable.sql
V1__createTable.sql
CREATE TABLE PLAN_MST (
plan_id serial
,plan_name varchar(100) NOT NULL
, plan_contents varchar(1000) NOT NULL
, meal_type char(1) NOT NULL
, plan_fee int NOT NULL
, room_id int NOT NULL
, image_photo varchar(100) NOT NULL
, create_user int NOT NULL
, create_date date NOT NULL
, update_user int NOT NULL
, update_date date NOT NULL
, del_flg char(1) NOT NULL
, PRIMARY KEY (plan_id)
) ;
CREATE TABLE ROOM_MST (
room_id serial
,room_type char(1) NOT NULL
,room_rank char(1) NOT NULL
,smoking_type char(1) NOT NULL
,bathroom_type char(1) NOT NULL
, guest_capacity int NOT NULL
, create_user int NOT NULL
, create_date date NOT NULL
, update_user int NOT NULL
, update_date date NOT NULL
, del_flg char(1) NOT NULL
, PRIMARY KEY (room_id)
) ;
CREATE TABLE CALENDER_TABLE (
date date
,date_type char(1) NOT NULL
,room_id int NOT NULL
,total_rooms int NOT NULL
,vacancy_rooms int NOT NULL
, room_fee int NOT NULL
, create_user int NOT NULL
, create_date date NOT NULL
, update_user int NOT NULL
, update_date date NOT NULL
, del_flg char(1) NOT NULL
, PRIMARY KEY (date, room_id)
) ;
V2__insertData.sql
V2__insertData.sql
/*プラン*/
INSERT INTO PLAN_MST (plan_id,plan_name,plan_contents,meal_type,plan_fee,room_id,image_photo,create_user,create_date,update_user,update_date,del_flg) VALUES (1,'【スウィートルーム】禁煙','70平米以上のスイートルームでゆったりとお寛ぎください。','3',30000,'1','room1.jpg',0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO PLAN_MST (plan_id,plan_name,plan_contents,meal_type,plan_fee,room_id,image_photo,create_user,create_date,update_user,update_date,del_flg) VALUES (2,'【スウィートルーム】喫煙','70平米以上のスイートルームでゆったりとお寛ぎください。','3',30000,'2','room2.jpg',0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO PLAN_MST (plan_id,plan_name,plan_contents,meal_type,plan_fee,room_id,image_photo,create_user,create_date,update_user,update_date,del_flg) VALUES (3,'【スタンダードプラン】洋室 素泊まり','ビジネス・観光・一人旅にご利用いただいているベーシックな宿泊プランです。','0',0,'3','room3.jpg',0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO PLAN_MST (plan_id,plan_name,plan_contents,meal_type,plan_fee,room_id,image_photo,create_user,create_date,update_user,update_date,del_flg) VALUES (4,'【スタンダードプラン】洋室 朝食付','ご夫婦・ご家族・一人旅の観光旅行におすすめのプランです。','1',2000,'3','room3.jpg',0,'2023/05/01',0,'2023/05/01','0');
/*部屋*/
INSERT INTO ROOM_MST (room_id,room_type,room_rank,smoking_type,bathroom_type,guest_capacity,create_user,create_date,update_user,update_date,del_flg) VALUES (1,'1','S','0','0',2,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO ROOM_MST (room_id,room_type,room_rank,smoking_type,bathroom_type,guest_capacity,create_user,create_date,update_user,update_date,del_flg) VALUES (2,'1','S','1','0',2,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO ROOM_MST (room_id,room_type,room_rank,smoking_type,bathroom_type,guest_capacity,create_user,create_date,update_user,update_date,del_flg) VALUES (3,'0','A','0','0',2,0,'2023/05/01',0,'2023/05/01','0');
/*カレンダー*/
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/01','0',1,10,5,20000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/02','0',1,10,5,20000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/03','1',1,10,5,25000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/04','1',1,10,5,25000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/05','1',1,10,5,25000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/06','1',1,10,10,25000,0,'2023/05/01',0,'2023/05/01','0');
…省略
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/01','0',2,10,5,20000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/02','0',2,10,5,20000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/03','1',2,10,5,25000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/04','1',2,10,5,25000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/05','1',2,10,5,25000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/06','1',2,10,10,25000,0,'2023/05/01',0,'2023/05/01','0');
…省略
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/01','0',3,10,5,15000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/02','0',3,10,5,15000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/03','1',3,10,5,20000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/04','1',3,10,5,20000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/05','1',3,10,5,20000,0,'2023/05/01',0,'2023/05/01','0');
INSERT INTO CALENDER_TABLE (date,date_type,room_id,total_rooms,vacancy_rooms,room_fee,create_user,create_date,update_user,update_date,del_flg) VALUES ('2023/05/06','1',3,10,10,20000,0,'2023/05/01',0,'2023/05/01','0');
…省略
domain
Plan.java
Plan.java
package com.example.domain;
import lombok.Data;
@Data
public class Plan {
private Integer planId;
private String planName;
private String planContents;
private String mealType;
private Integer planFee;
private Room room;
private String ImagePhoto;
}
Room.java
Room.java
package com.example.domain;
import java.util.List;
import lombok.Data;
@Data
public class Room {
private Integer roomId;
private String roomType;
private String roomRank;
private String smokingType;
private String bathroomType;
private Integer guestCapacity;
private List<Calender> vacancyRoomCalender;
}
Calender.java
Calender.java
package com.example.domain;
import java.util.Date;
import lombok.Data;
@Data
public class Calender {
private Date date;
private String dateType;
private Integer roomId;
private Integer totalRooms;
private Integer vacancyRooms;
private Integer roomFee;
private Integer accomodationFee;
}
repository
PlanMstRepository.java
PlanMstRepository.java
package com.example.repository;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import com.example.domain.Plan;
import com.example.domain.Room;
@Repository
public class PlanMstRepository {
@Autowired
private NamedParameterJdbcTemplate template;
private final static RowMapper<Plan> PLAN_ROOM_ROW_MAPPER=
(rs,i) -> {
Plan plan = new Plan();
plan.setPlanId(rs.getInt("plan_id"));
plan.setPlanName(rs.getString("plan_name"));
plan.setPlanContents(rs.getString("plan_contents"));
plan.setMealType(rs.getString("meal_type"));
plan.setPlanFee(rs.getInt("plan_fee"));
plan.setImagePhoto(rs.getString("image_photo"));
Room room = new Room();
room.setRoomId(rs.getInt("room_id"));
room.setRoomType(rs.getString("room_type"));
room.setRoomRank(rs.getString("room_rank"));
room.setSmokingType(rs.getString("smoking_type"));
room.setBathroomType(rs.getString("bathroom_type"));
room.setGuestCapacity(rs.getInt("guest_capacity"));
plan.setRoom(room);
return plan;
};
/**
* プラン全件select
* @param planId
* @return plan
*/
public List<Plan> selectAllPlan() {
String sql="SELECT "+
"p.plan_id, "+
"p.plan_name, "+
"p.plan_contents, "+
"CASE p.meal_type "+
"WHEN '0' THEN '素泊まり' "+
"WHEN '1' THEN '朝食付' "+
"WHEN '2' THEN '夕食付' "+
"WHEN '3' THEN '朝夕付' "+
"ELSE '登録なし' "+
"END as meal_type, "+
"p.plan_fee, "+
"p.image_photo, "+
"r.room_id, "+
"CASE r.room_type "+
"WHEN '0' THEN '和室' "+
"WHEN '1' THEN '洋室' "+
"ELSE '登録なし' "+
"END as room_type, "+
"CASE r.bathroom_type "+
"WHEN '0' THEN '客室露天風呂付' "+
"WHEN '1' THEN '客室露天風呂なし' "+
"ELSE '登録なし' "+
"END as bathroom_type, "+
"CASE r.smoking_type "+
"WHEN '0' THEN '禁煙' "+
"WHEN '1' THEN '喫煙' "+
"ELSE '登録なし' "+
"END as smoking_type, "+
"CASE r.room_rank "+
"WHEN 'S' THEN 'スイート' "+
"WHEN 'A' THEN 'スタンダード' "+
"ELSE '登録なし' "+
"END as room_rank, "+
"r.guest_capacity "+
"FROM PLAN_MST as p "+
"JOIN ROOM_MST as r ON p.room_id = r.room_id "+
"WHERE p.del_flg='0' AND r.del_flg='0' "+
"ORDER BY p.plan_id";
return template.query(sql,PLAN_ROOM_ROW_MAPPER);
}
}
CalenderTableRepository.java
CalenderTableRepository.java
package com.example.repository;
import java.time.LocalDate;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;
import com.example.domain.Calender;
@Repository
public class CalenderTableRepository {
@Autowired
private NamedParameterJdbcTemplate template;
private static final RowMapper<Calender> CALENDER_ROW_MAPPER
= (rs,i) -> {
Calender calender = new Calender();
calender.setDate(rs.getDate("date"));
calender.setRoomId(rs.getInt("room_id"));
calender.setVacancyRooms(rs.getInt("vacancy_rooms"));
calender.setAccomodationFee(rs.getInt("accomondation_fee"));
return calender;
};
/**
* 各日付の空室状況・料金を取得するSELECT文
* @param startDate
* @param endDate
* @param planId
* @return 日付・部屋ID・空室数・宿泊料金
*/
public List<Calender> selectByPlanId(LocalDate startDate, LocalDate endDate, Integer planId){
String sql = "SELECT c.date, "
+ "c.room_id, "
+ "c.vacancy_rooms, "
+ "(c.room_fee + p.plan_fee) as accomondation_fee "
+ "FROM PLAN_MST as p "
+ "JOIN (SELECT date, room_id, vacancy_rooms, room_fee "
+ "FROM CALENDER_TABLE WHERE date BETWEEN :startDate AND :endDate AND del_flg='0') as c "
+ "ON p.room_id = c.room_id "
+"WHERE p.plan_id = :planId AND p.del_flg='0' ORDER BY c.date";
SqlParameterSource param = new MapSqlParameterSource().addValue("startDate", startDate).addValue("endDate", endDate).addValue("planId", planId);
return template.query(sql,param,CALENDER_ROW_MAPPER);
}
}
service
PlanListService.java
PlanListService.java
package com.example.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.domain.Plan;
import com.example.repository.PlanMstRepository;
@Service
@Transactional
public class PlanListService {
@Autowired
PlanMstRepository planMstRepository;
//プラン全件取得
public List<Plan> search(){
return planMstRepository.selectAllPlan();
}
}
VacancyRoomService.java
VacancyRoomService.java
package com.example.service;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.domain.Calender;
import com.example.repository.CalenderTableRepository;
@Service
@Transactional
public class VacancyRoomService {
@Autowired
CalenderTableRepository calenderTableRepository;
//1か月単位で空室状況を取得する
public List<Calender> searchCalender(String month, String planId){
String strStartOfMonth = month +"-01";
LocalDate startDate = LocalDate.parse(strStartOfMonth, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
LocalDate endDate = startDate.withDayOfMonth(startDate.lengthOfMonth());
List<Calender> vacancyRoomCalender = calenderTableRepository.selectByPlanId(startDate, endDate, Integer.parseInt(planId));
//カレンダー表示するために月初の前の空白を追加
int beforeBlank= startDate.getDayOfWeek().getValue() - 1;//前月の最終日の曜日
for(int i =0; i<=beforeBlank; i++) {
Calender calender = null;
vacancyRoomCalender.add(0, calender);
}
return vacancyRoomCalender;
}
}
controller
PlanListController.java
PlanListController.java
package com.example.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.domain.Calender;
import com.example.service.PlanListService;
import com.example.service.VacancyRoomService;
@Controller
@RequestMapping("/plan")
public class PlanListController {
@Autowired
private PlanListService planListService;
@Autowired
private VacancyRoomService vacancyRoomService;
//プラン一覧を表示する
@RequestMapping("/")
public String planPage(Model model) {
model.addAttribute("planList", planListService.search());
return "plans_list";
}
//空室カレンダーを取得
@GetMapping("/vacancyRoom/search")
@ResponseBody
public Map<String, List<Calender>> search(@RequestParam("month") String month,
@RequestParam("planId") String planId){
Map <String, List<Calender>> map = new HashMap<>();
map.put("calenderList", vacancyRoomService.searchCalender(month, planId));
return map;
}
}
2.画面
親画面
- 「空室確認」ボタンを押すと、初期値として今月の空室状況が表示される。
plans_list.html
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.2/css/all.css" integrity="sha384-/rXc/GQVaYpyDdyxK+ecHPVYJSN9bmVFBvjA/9eOB+pb3F2w2N6fc5qB9Ew5yIns" crossorigin="anonymous">
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<link rel="stylesheet" th:href="@{/css/common.css}" />
<title>プラン検索</title>
</head>
<body>
<main>
<!-- プラン一覧 -->
<table class="plan" th:each="plan:${planList}">
<tr><th colspan="2" th:text="${plan.planName}"></th></tr>
<tr><td rowspan="3" class="col-5"><img th:src="'/img/rooms/'+${plan.imagePhoto}"></td>
<td th:text="${plan.mealType}+' '+${plan.room.roomType}+' '+${plan.room.roomRank}+' '+${plan.room.smokingType}+' '+${plan.room.bathroomType}"></td></tr>
<tr><td th:text="${plan.planContents}"></td></tr>
<tr><td><button class="btn btn-vacancy-room" type="button" data-toggle="modal" data-target="#vacancy-room-modal" th:attr="data-whatever=${plan.planId}">空室確認</button></td></tr>
</table>
<!-- 空室確認モーダル -->
<div class="modal fade" id="vacancy-room-modal" tabindex="-1" role="dialog" aria-labelledby="basicModal" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div th:insert="modal_vacancy_room :: fragment-vacancyRoomCalender"></div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-dismiss" data-dismiss="modal">閉じる</button>
</div>
</div>
</div>
</div>
</main>
<script>
window.addEventListener('DOMContentLoaded', function(){
$('#vacancy-room-modal').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget) //モーダルを呼び出すときに使われたボタンを取得
var planId = button.data('whatever') //data-whatever の値を取得
//monthに今月を設定
var today = new Date();
if((today.getMonth()+1) <10){
var month = today.getFullYear() +'-0'+ (today.getMonth()+1);//1-9日
}else{
var month = today.getFullYear() +'-'+ (today.getMonth()+1);
}
var modal = $(this) //モーダルを取得
modal.find('.modal-body input#planId').val(planId);//モーダル内のinputタグid="planId"にplanIdの値を渡す
modal.find('.modal-body input#month').val(month);//モーダル内のinputタグid="month"にmonthの値を渡す
});
$('#vacancy-room-modal').on('shown.bs.modal', function(){
searchVacancyRoom();//選択したプランの今月の空室状況を表示する。
});
});
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</body>
</html>
説明
<button class="btn btn-vacancy-room" type="button" data-toggle="modal" data-target="#vacancy-room-modal" th:attr="data-whatever=${plan.planId}">空室確認</button>
-
data-toggle="modal" data-target="#vacancy-room-modal"
と記述することで、クリックすると、id="vacancy-room-modal"を付与した部分が表示される。 - data-*属性でモーダルに値を渡すことができる。ここでは「data-whatever」を使用している。
-
th:attr="data-whatever=${plan.planId}"
と記述することでThymeleafでdata-*属性に値を設定することができる。
<div th:insert="modal_vacancy_room :: fragment-vacancyRoomCalender"></div>
- th:insert="子画面 :: 子画面内のth:fragmentの値"
- この部分にmodal_vacancy_room.htmlのth:fragment="fragment-vacancyRoomCalender"で囲った部分が挿入される。
$('#vacancy-room-modal').on('show.bs.modal', function (event) { … });
$('#vacancy-room-modal').on('shown.bs.modal', function(){ … });
-
show.bs.modal
:モーダルが呼び出された時の処理 -
shown.bs.modal
:モーダルが画面表示された後の処理
子画面(モーダル)
- モーダルを別htmlに作成
- 非同期で空室状況を取得しカレンダー形式で表示している。
modal_vacancy_room.html
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>空室確認</title>
</head>
<body>
<div th:fragment="fragment-vacancyRoomCalender">
<div class="modal-body">
<div class="month-form">
<input type="month" name="month" id="month" class="form-control" onchange="searchVacancyRoom()">
</div>
<input type="hidden" name="planId" id="planId">
※1泊のみした場合のおひとり当たりの料金です。
<div id="vacancyRoomCalender"></div>
</div>
<script>
//空室状況を表示する
function searchVacancyRoom(){
var monthVal =$('#month').val();
var planIdVal =$('#planId').val();
$.ajax({
url : '/plan/vacancyRoom/search',
type : 'GET',
data: {
month: monthVal,
planId: planIdVal,
},
dataType : 'json'
}).done(function(data) {
var vacancyRoomCalender_html ='<table class="vacancy-room"><tr><th class="sunday">日</th><th>月</th><th>火</th><th>水</th><th>木</th><th>金</th><th class="saturday">土</th></tr>';
$.each(data.calenderList, function(i, calender){
if(calender != null){
var date = calender.date;
var day = date.slice(-2);//カレンダー表示用に日付だけ取り出す
if(day.slice(0,1)=='0'){
day = day.slice(-1);//1-9日までは一桁で取り出す
}
var vacancyRooms = calender.vacancyRooms;//空室数
if(vacancyRooms == 0){
var vacancyRooms_html = '<div style="color:red">満室</div>';
var accomodationFee_html = '';//満室の時は料金を表示しない
}else{
var vacancyRooms_html = vacancyRooms+'室';
var accomodationFee_html = calender.accomodationFee.toLocaleString()+'円';
}
var today = new Date();
today.setDate(today.getDate());//今日
var yyyy = today.getFullYear();
var mm = ("0"+(today.getMonth()+1)).slice(-2);
var dd = ("0"+today.getDate()).slice(-2);
var todayVal=yyyy+'-'+mm+'-'+dd;//今日の年月日
if(calender.date<todayVal){
//昨日以前の空室数、料金は表示しない
var vacancyRooms_html = '-';
var accomodationFee_html = '';
}
if(i%7==0){
//日曜日
vacancyRoomCalender_html +='<tr><td class="sunday">'+day+'<br>'+vacancyRooms_html+'<br>'+accomodationFee_html;
}else if(i%7==6){
//土曜日
vacancyRoomCalender_html +='<td class="saturday">'+day+'<br>'+vacancyRooms_html+'<br>'+accomodationFee_html;
}else{
//平日
vacancyRoomCalender_html +='<td>'+day+'<br>'+vacancyRooms_html+'<br>'+accomodationFee_html;
}
}else{//前後の空白部分
if(i%7==0){
vacancyRoomCalender_html +='<tr><td class="sunday"></td>';
}else if(i%7==6){
vacancyRoomCalender_html +='<td class="saturday"></td></tr>';
}else{
vacancyRoomCalender_html +='<td></td>';
}
}
});
vacancyRoomCalender_html +='</table>';
$('#vacancyRoomCalender').html(vacancyRoomCalender_html);
});
}
</script>
</div><!-- <div th:fragment="fragment-vacancyRoomCalender>"の最後 -->
</body>
</html>
説明
- 親画面に挿入したい部分を
<div th:fragment="fragment-vacancyRoomCalender">…</div>
で囲む。
css
common.css
common.css
table.plan {
border-collapse: collapse;
border-spacing: 5px;
padding: 0;
width: 80%;
margin: 1% auto auto auto;
border-spacing: 10px 10px;
}
table.plan tr{
border-bottom: solid 2px white;
}
table.plan th{
position: relative;
text-align: left;
background-color: #48d1cc;
color: white;
padding: 1% 1% 1% 1%;
}
table.plan td{
text-align: left;
background-color: #f0f8ff;
padding: 1% 1% 1% 1%;
}
.btn-vacancy-room{
width:100%;
margin: auto;
margin-left: auto;
margin-right: auto;
color:black;
background-color:#ffd700;
border-color:#ffd700;
}
img{
width:100%;
height:100%;
}
table.vacancy-room {
border-collapse: collapse;
border-spacing: 5px;
padding: 0;
width: 80%;
margin: 1% auto auto auto;
}
table.vacancy-room tr{
border: solid 2px #a9a9a9;
}
table.vacancy-room th{
position: relative;
text-align: left;
width: 10%;
background-color: #90ee90;
color: white;
text-align: center;
padding: 1% 1% 1% 1%;
border: solid 2px #a9a9a9;
}
table.vacancy-room th.sunday{
position: relative;
text-align: left;
width: 10%;
background-color: #ff6347;
color: white;
text-align: center;
padding: 1% 1% 1% 1%;
}
table.vacancy-room th.saturday{
position: relative;
text-align: left;
width: 10%;
background-color: #87cefa;
color: white;
text-align: center;
padding: 1% 1% 1% 1%;
}
table.vacancy-room td{
text-align: center;
width: 10%;
background-color: #ffffff;
padding: 1% 1% 1% 1%;
border: solid 2px #a9a9a9;
}
table.vacancy-room td.sunday{
width: 10%;
background-color: #ffe4c4;
padding: 1% 1% 1% 1%;
}
table.vacancy-room td.saturday{
width: 10%;
background-color: #f0f8ff;
padding: 1% 1% 1% 1%;
}
.month-form{
width: 20%;
}
body {
width: 100%;
display: flex;
flex-flow: column;
min-height: 100vh;
}
main {
flex: 1;
}