【食事内容管理Webアプリ】メモ内容の表示

前回までのあらすじ&今回の目標

前回は、データの登録処理を実装しました。それによりモーダル画面からメニューの追加ができるようになりました。

○メニューの追加
メニューの追加

ただ現在の画面から確認できる内容は「日付」、「食事の種類 (色)」、「料理名」となっており、モーダル画面から入力可能な「メモ」については確認できない状態です。

そのため今回は「メモ」の内容を画面上から確認できるようになることを目標に進めていきます。

解決方針

カレンダーに表示されているメニューは「FullCalendar」のイベントオブジェクトであるため、メモを表示する場合はその内容をイベントに持たせる必要があります。

公式のドキュメントを確認したところ、titleプロパティ以外に文字列を表示させるようなプロパティはなかったのですが、独自のプロパティを持たせることができるextendedPropsプロパティというものが用意されていたので、今回はメモの内容をこのプロパティに持たせつつ、画面への表示処理は自前で実装していこうと思います。

FullCalendarのextendedPropsプロパティ

extendedPropsプロパティは「FullCalendar」のイベントオブジェクトに設定できるプロパティの一種です。

このプロパティには複数の独自プロパティを設定でき、設定後はJSONのデータ形式のようにキーのセットでデータを保持します。

JavaScript (extendedPropsの記述方法)
events: [
    {
      extendedProps: {
        prop1: 'メモ1',
        prop2: 'メモ2'
      }
      // {prop1: "メモ1", prop2: "メモ2"}
  ]

このプロパティを使用して各イベントオブジェクトにメモの内容を持たせておけば後で画面に表示できそうですね。

eventMouseEnter/eventMouseLeaveプロパティ

次にextendedPropsプロパティで保持したメモの内容を表示する方法を考えます。

料理名のように最初から表示されていると画面がごちゃごちゃしてしまいそうなので、マウスポインタが各イベントオブジェクト上にきた時のみ表示させる仕様で進めていきます。

各イベントオブジェクトのマウスオーバー/アウト時に処理を呼び出す場合はeventMouseEnter/eventMouseLeaveプロパティを使用します。

上記二つのプロパティには関数を設定でき、設定した関数の引数にはイベントオブジェクトの情報(eventプロパティ)などを持ったJavaScriptオブジェクトが渡されます。

そのため、マウスオーバー/アウトしたイベントオブジェクトに対してメモの表示/非表示処理を行う場合は下記のように記述すれば良さそうです。

JavaScript (eventMouseEnter/eventMouseLeaveの記述方法)
var calendar = new Calendar(calendarEl, {
  events: [
    {
      eventMouseEnter: function(mouseEnterInfo) {
        let memo = mouseEnterInfo.event.extendedProps.memo; // メモの内容取得
        // メモ表示処理
      },
      eventMouseLeave: function(mouseLeaveInfo) {
        // メモ非表示処理
      }
    }
  ]
})

実装

以上より実装の方針が固まったため、実装を開始していきます。

実現するために必要な修正は下記の通りです。

  1. イベントオブジェクトにextendedPropsプロパティを持たせるためにEventBeanクラスにextendedPropsフィールドを追加する
  2. 「menuテーブル」からメモの内容を取得しEventBeanオブジェクトに格納するようRepositoryクラスおよびSQLを修正する
  3. イベントオブジェクトに持たせたメモを表示/非表示するための処理をJavaScriptに追加する

EventBeanクラスの修正

EventBeanクラスにextendedPropsフィールドを追加します。

extendedPropsプロパティはキーのセットで持つため、このフィールドの型はMap<String, String>とします。(8行目)

そして値を設定する際に記述を簡略化できるよう例に従って引数をフィールドにセットした後に自身の参照値を返す関数を追加します。(25~31行目)

EventBean.java
@Data
public class EventBean {

	private String start;
	private String title;
	private String backgroundColor;
	private String borderColor = "white";
	private Map<String, String> extendedProps;
	
	public EventBean start(String start) {
		this.start = start;
		return this;
	}
	
	public EventBean title(String title) {
		this.title = title;
		return this;
	}
	
	public EventBean backgroundColor(String backgroundColor) {
		this.backgroundColor = backgroundColor;
		return this;
	}
	
	public EventBean memo(String memo) {
		if (memo != null) {
			extendedProps = new HashMap<>();
			extendedProps.put("memo", memo);
		}
		return this;
	}
}

Repositoryクラスの修正

RepositoryクラスはServiceクラスからデータ取得の要求がきた時に、データベースから取得したデータをeventsオブジェクトに格納して返します。

今回はeventsオブジェクトに新たにメモの内容を格納するために、格納処理部分を修正します。(17行目)

CalendarRepositoryImpl.java (修正部分抜粋)
@Repository
public class CalendarRepositoryImpl implements CalendarRepository {
	
	@Autowired
	JdbcTemplate jdbcTemplate;

	@Override
	public List<EventBean> getEvents(String year, String month) {
		String date = year + month + "01";
		
		List<EventBean> events = jdbcTemplate.query(
				SqlConst.getQueryFromXml(SqlConst.MENU_FOR_EACH_MONTH),
				(rs, rowNum) -> new EventBean()
										.start(rs.getString("start"))
										.title(rs.getString("title"))
										.backgroundColor(rs.getString("backgroundColor"))
										.memo(rs.getString("memo")),
				date,
				date);
		
		return events;
	}
	
	...
	
}

SQLの修正

SQLは「menuテーブル」に追加した項目memoを取得項目に追加します。(8行目)

memo.xml
<query id="forEachMonth" comment="各月のメニューを取得">
  <![CDATA[
  	select
  		menu.eating_date start
  		, dishes.dish_name title
  		, menu.meal_type_code code
  		, meal_types.color backgroundColor
  		, menu.memo
  	from
  		menu
  	inner join
  		dishes
  	on
  		menu.dish_code = dishes.dish_code
  	inner join
  		meal_types
  	on
  		menu.meal_type_code = meal_types.meal_type_code
  	where
  		menu.eating_date between str_to_date(?, '%Y%m%d') and last_day(str_to_date(?, '%Y%m%d'))
  	order by
  		start
  		, code
  ]]>
</query>

JavaScriptの修正

JavaScriptは「eventMouseEnter/eventMouseLeaveプロパティの追加」と「メモの表示/非表示処理の追加」を行います。

各プロパティの関数は別に下で定義したものを渡しています。(13、14行目)

また「メモの表示処理」では要素を新たに作成し、その要素のテキストにイベントオブジェクトが持っているmemoを設定、「メモの非表示処理」では作成した要素の削除を行う設計とします。

editcalendar.js
function initializeCalendar(events) {
	let calendarEl = document.getElementById('calendar');
	let calendar = new FullCalendar.Calendar(calendarEl, {
		customButtons: {
			new: {
				text: "new",
				click: showRegistrationModal
			}
		},
		headerToolbar: {
			right: 'new today prev,next'
		},
		eventMouseEnter: showMemo,
		eventMouseLeave: closeMemo,
		contentHeight: "auto",
		events
	});
	calendar.render();
	
	let closer = document.getElementById('closer');
	if (closer) {
		closer.addEventListener('click', closeRegistrationModal);
	}
	
}

...

cssの修正

メモの表示領域がわかりやすいようにcssで見た目を整えます。後々リッチな見た目に変更したいですね。

CSS
@charset "UTF-8";
body {
  margin: 0;
}

#memo-window {
	display: block;
	padding: 2px;
	width: 160px;
	background: rgb(221, 225, 240);
	border-radius: 10px;
	border: 1px solid black;
	z-index: 1;
}

動作確認

上記修正が完了したため、動作の確認を行います。前回追加したおにぎりの具についてのメモが表示されるかどうか確認してみましょう。

○メモの表示処理
メモの表示処理

○メモの非表示処理
メモの非表示処理

マウスオーバー/アウトで表示/非表示が切り替わることが確認できました。これで画面上でのメモの内容確認も可能になりましたね!

今後の予定

今回は登録したメモの内容を画面上から確認できるように修正を行いました。

話は少し変わりますが、FullCalendarにはどうやらイベントオブジェクトのドラッグ&ドロップ機能があるようです。

メニューがドラッグ&ドロップできたら、登録作業がかなり楽になりますよね!

ということで、次回はFullCalendarのドラッグ&ドロップ機能を調査・実装していきたいと思います。

今夜は豆腐ハンバーグです。それではまた。。

コメント

タイトルとURLをコピーしました