MENU

【FullCalendar】FullCalendarとThymeleafで色々やってみた【SpringBoot】

こんにちは、くまごろーです。
今日は使うのはjQueryプラグイン、「FullCalendar」です。

fullcalendar.io

今作成中の家計簿アプリで、こんな機能を実装したかったんですよね。

  • カレンダーに日ごとの支出/収入合計を表示する
  • 金額をクリックすると、その日の出入金履歴一覧に遷移する

全然やり方がわからなくてかなり苦戦したんですが、今日やっと形になったのでまとめていきます。
 
 
 

1 おおまかな仕組み

今回の方法はこんな感じ。
(1)FullCalendar用のmodelを作成
(2)DBから出入金履歴を検索(収入と支出に分けた上で、日付毎に集計)
(3)(2)を(1)で作成したmodelに代入
(4)(2)のデータ(model)をRestControllerに渡す。
(5)出入金の文字列にリンクタグを埋め込む。
(6)データをJson化し、FullCalendarに渡す。
(7)FullCalendarに表示
 
 

2 modelクラスを作成する

 
DBから検索したデータをFullCalendarに登録できる形式で受け取るために、専用のクラスを作成します。
<DailySummary.java

package com.example.demo.model.beans;

import java.io.Serializable;

import lombok.Data;

@Data
public class DailySummary implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private String title;
	private String start;
	private String end;
	
	public DailySummary(String title, String start, String end) {
		super();
		this.title = title;
		this.start = start;
		this.end = end;
	}
	
	public DailySummary(Object[] objects) {
        this((String) objects[0], (String) objects[1], (String)objects[1]);
    }
	
}

 
 
 
3 RestControllerを作成
 
ここではControllerではなく、RestControllerを使います。ControllerはHTMLファイルを戻り値にするのに対し、RestControllerは「JSONXML等の文字列を返すWebAPI用のコントローラクラスに使用するアノテーション」らしい。

参考:
【Spring】@Controllerと@RestControllerの違い | Memento Mori Blog

<RestCalendarController.java

@RequiredArgsConstructor
@RestController
@RequestMapping("/api/event")
public class RestCalendarController {

  @Autowired
  private final MoneyRecordRepository mRepository;

  @GetMapping(value = "/all")
  public String getDailySummaries(Authentication loginUser) {
    String jsonMsg = null;
    try {
      //支出の日ごと合計を算出するメソッド
     List<DailySummary> dailyExpense = mRepository.findExpense(loginUser.getName());
     for(int i = 0; i < dailyExpense.size(); i++) {
       DailySummary ds = dailyExpense.get(i);
       //この時点でリンクタグを埋め込んでしまう *1
       String title = "<a class=\"expense\" href=\"/Records/" + ds.getStart() + "\">" +"支出  " + ds.getTitle() + "</a>";
       ds.setTitle(title);
      }
     //収入の日ごと合計を算出するメソッド
     List<DailySummary> dailyIncome = mRepository.findIncome(loginUser.getName());
     for(int i = 0; i < dailyIncome.size(); i++) {
      DailySummary ds = dailyIncome.get(i);
    //この時点でリンクタグを埋め込む
      String title = "<a class=\"income\" href=\"/Records/" + ds.getStart() + "\">" +"収入  " + ds.getTitle() + "</a>";
      ds.setTitle(title);
       }
            
       //支出と収入のListを一つにまとめる
       List<DailySummary> dailySummaries= new ArrayList<DailySummary>();
       for(int i = 0; i < dailyExpense.size(); i++) {
         dailySummaries.add(dailyExpense.get(i));
       }
       for(int i = 0; i < dailyIncome.size(); i++) {
         dailySummaries.add(dailyIncome.get(i));
       }
            
            // FullCalendarにエンコード済み文字列を渡す
       ObjectMapper mapper = new ObjectMapper();
       jsonMsg =  mapper.writerWithDefaultPrettyPrinter().writeValueAsString(dailySummaries);
    }catch(IOException ioex){
       System.out.println(ioex.getMessage());
    
    }return jsonMsg;
  }
}

とにもかくにも、RestControllerを呼び出せばJsonが返ってくるようになりました。


※1 リンクタグについて

String title = "<a class=\"expense\" href=\"/Records/" + ds.getStart() + "\">" +"支出  " + ds.getTitle() + "</a>";

このコードは、viewに渡されるとこんな感じになります。

<a class="expense" href="/Records/2021-01-02">支出 2000円</a>

最初、「href」を「th:href」にして、URL部分に変数を使ってたんだけど、これはうまくいかなかった。
タグ自体はhtmlタグとして認識されていたんだけど、リンクが上手く張られませんでした。



4 viewファイルにカレンダーを表示する

CDN

<link rel='stylesheet'
	th:href="@{/webjars/fullcalendar/3.5.1/dist/fullcalendar.css}">
<script th:src="@{/webjars/jquery/3.5.1/jquery.min.js}"></script>
<script th:src="@{webjars/moment/2.19.1/min/moment.min.js}"></script>
<script th:src="@{webjars/fullcalendar/3.5.1/dist/fullcalendar.js}"></script>

タグ自体はたったの一行。

<div class="calendar" id='calendar'></div>

 
 
 
5 JavaScript部分
 
JavaScript部分はこんな感じ。

<script>
$(document).ready(
function() {
  $('#calendar').fullCalendar(
  {
    events : {
      url : '/api/event/all' //ここにRestControllerを呼び出すurlを記載
    },
    //イベントの色を変える
    eventColor : '#FFFFFF',
    eventRender : function(event, element) {
      element.css("font-size", "0.9em");
      element.css("padding", "5px");
      //タグをhtmlタグとして認識するようにする *2
      element.find('span.fc-title').html(
      element.find('span.fc-title').text());
    }
  });
});
</script>

※2
RestControllerで埋め込んだhtmlタグは、何も指定しないとただのテキストとして認識されてしまいます。

 eventRender : function(event, element) {
      element.find('span.fc-title').html(element.find('span.fc-title').text());
    }

このコードを追加するとhtmlタグとして扱ってくれるようになる。
 
 
 
6 おまけ:css
 
今回、支出と収入で色分けしたかったので、支出と収入別々に集計して、RestControllerで個別にタグをつけました。
FullCalendarではJavaScriptでイベントの文字色を設定できるのですが、色分けしたいのでcssで設定します。

.expense {
	color: red;
}
.income{
	color:green;
}

 
 
 
7 結果
 
f:id:kumaGoro_95:20210121154551p:plain
f:id:kumaGoro_95:20210121154600p:plain

無事、支出/収入合計額の表示・リンクを張ることができました。
 
  

8 参考