1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Bootstrap】【Thymeleaf】モーダルを別htmlに切り出す

Posted at

概要

  • Thymeleafのfragment属性を使ってモーダルを子要素として切り出す。
  • モーダルはBootstrap4を使用して作成する。

完成イメージ

宿泊プラン一覧
image.png

「空室確認」を押すとその宿泊プランの空室状況が表示される
image.png

環境

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}+'&nbsp;&nbsp;'+${plan.room.roomType}+'&nbsp;&nbsp;'+${plan.room.roomRank}+'&nbsp;&nbsp;'+${plan.room.smokingType}+'&nbsp;&nbsp;'+${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;
}

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?