こんにちは、くまごろーです。
今日は使うのはjQueryのプラグイン、「FullCalendar」です。
今作成中の家計簿アプリで、こんな機能を実装したかったんですよね。
- カレンダーに日ごとの支出/収入合計を表示する
- 金額をクリックすると、その日の出入金履歴一覧に遷移する
全然やり方がわからなくてかなり苦戦したんですが、今日やっと形になったのでまとめていきます。
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は「JSONやXML等の文字列を返す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 結果
無事、支出/収入合計額の表示・リンクを張ることができました。
8 参考