ThymeleafでEnumからセレクトボックスを作成する
【概要】
ThymeleafでEnum型を使ってセレクトボックスを表示させます。
今回はJerseyMVCとThymeleafを連携させていますが、
セレクトボックスはJerseryやJAX-RSに依存していないはずです。
なので、SpringBootなどでも動作すると思います。
(動作確認はしていないですが。)
【ThymeleafとJerserMVCの連携】
基本的には こちらの設定と同じです。
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>jp.glory</groupId> <artifactId>JerseyMVC-Thymeleaf</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>JerseyMVC-Thymeleaf</name> <properties> <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>javax.enterprise</groupId> <artifactId>cdi-api</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-core</artifactId> <version>1.19</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-servlet</artifactId> <version>1.19</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-mvc</artifactId> <version>2.19</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>2.1.4.RELEASE</version> <scope>compile</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> <compilerArguments> <endorseddirs>${endorsed.dir}</endorseddirs> </compilerArguments> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.6</version> <executions> <execution> <phase>validate</phase> <goals> <goal>copy</goal> </goals> <configuration> <outputDirectory>${endorsed.dir}</outputDirectory> <silent>true</silent> <artifactItems> <artifactItem> <groupId>javax</groupId> <artifactId>javaee-endorsed-api</artifactId> <version>7.0</version> <type>jar</type> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
[TemplateProcessor]
package jp.glory.jerseymvc.thymeleaf.settings; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.Provider; import org.glassfish.jersey.server.mvc.Viewable; import org.glassfish.jersey.server.mvc.spi.TemplateProcessor; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.WebContext; import org.thymeleaf.templateresolver.ServletContextTemplateResolver; import org.thymeleaf.templateresolver.TemplateResolver; @Provider public class ThymeleafViewProcessor implements TemplateProcessor<String> { @Context private HttpServletRequest request; @Context private HttpServletResponse response; @Context private ServletContext servletContext; private final TemplateEngine templateEngine; public ThymeleafViewProcessor() { TemplateResolver resolver = new ServletContextTemplateResolver(); resolver.setPrefix("/WEB-INF/view/"); resolver.setSuffix(".html"); resolver.setTemplateMode("HTML5"); resolver.setCacheTTLMs(3600000L); templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(resolver); } @Override public String resolve(String name, MediaType mediaType) { return name; } @Override public void writeTo(String templateReference, Viewable viewable, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream out) throws IOException { WebContext context = new WebContext(request, response, servletContext); context.setVariable("item", viewable.getModel()); Writer writer = new OutputStreamWriter(out); templateEngine.process(templateReference, context, writer); writer.flush(); } }
[ResourceConfig]
package jp.glory.jerseymvc.thymeleaf.application; import javax.ws.rs.ApplicationPath; import org.glassfish.jersey.filter.LoggingFilter; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.mvc.MvcFeature; import jp.glory.jerseymvc.thymeleaf.settings.ThymeleafViewProcessor; @ApplicationPath("service") public class ApplicationSetting extends ResourceConfig { public ApplicationSetting() { packages(this.getClass().getPackage().getName()); register(ThymeleafViewProcessor.class); register(MvcFeature.class); register(LoggingFilter.class); } }
【Java側の実装】
ここからようやく今回の本題と関わるところです。
まずはJava側の実装です。
構成としてはEnum、表示用のBean、リソースクラスです。
[Enum]
package jp.glory.jerseymvc.thymeleaf.application.resource; public enum Category { ProgrammingLanguage(1, "プログラム言語"), Server(2, "サーバ"), Tool(3, "ツール"); private final int id; private final String label; private Category(int id, String label) { this.id = id; this.label = label; } public int getId() { return id; } public String getLabel() { return label; } }
idとlabelを持ったEnumです。
特に何も特別なことはしていないです。
[表示用のBean]
package jp.glory.jerseymvc.thymeleaf.application.resource; import java.util.Arrays; import java.util.List; public class EnumView { private List<Category> categorys = null; private Category selected = null; public List<Category> getCategorys() { return categorys; } public void setCategorys(List<Category> categorys) { this.categorys = categorys; } public Category getSelected() { return selected; } public void setSelected(Category selected) { this.selected = selected; } }
表示用のBeanはセレクトボックスの選択肢となるEnumのListと
画面が表示されたタイミングで選択されているEnumを持ちます。
[リソースクラス]
package jp.glory.jerseymvc.thymeleaf.application.resource; import java.util.Arrays; import javax.enterprise.context.RequestScoped; import javax.ws.rs.GET; import javax.ws.rs.Path; import org.glassfish.jersey.server.mvc.Viewable; @RequestScoped @Path("enum") public class EnumSample { @GET public Viewable view() { EnumView view = new EnumView(); view.setCategorys(Arrays.asList(Category.values())); view.setSelected(Category.Tool); return new Viewable("/enum", view); } }
リソースクラスでは先ほどの表示用のBeanにデータを設定しています。
設定したBeanをモデルとしてViewableオブジェクトを返しています。
[Thymeleaf]
今回のメインとなる内容です。
<!DOCTYPE html> <html> <head> <title>Enum</title> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> </head> <body> <select> <option th:each="category : ${item.categorys}" th:selected="${item.selected.id == category.id}" th:value="${category.id}" th:text="${category.label}">hoge</option> </select> </body> </html>
【表示結果】
[ソース]
実際に生成されたソースはこちらです。
<!DOCTYPE html> <html> <head> <title>Enum</title> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> </head> <body> <select> <option value="1">プログラム言語</option> <option value="2">サーバ</option> <option selected="selected" value="3">ツール</option> </select> </body> </html>
選択肢[ツール]の部分だけ、selected属性が付与されています。
selected属性の値はselectedになるようです。
【まとめ】
Thymeleaf側でEnumを意識せずに使えて、個人的には便利だと思います。
optionタグ部分の属性部分が長くなってしまうのがちょっと気になりますが・・・
【変更】
2015/07/11
ApplicationSettingクラスの@ApplicationPathのパス設定が間違っていたので修正しました。