JSFでエラーフィールドの背景色を変える(3)
の続き。
前回の記事で 課題とした
https://github.com/system-sekkei/isolating-the-domain
のように 背景色というよりも、指定領域のスタイルを変更する という要件には応えられていません。
について、今回は取り組みたいと思います。
やりたいこと
- エラー対象に関連付けた任意の場所のスタイルを変更する
- (ついでに)
jsf:styleClass
を使わないHTML Friendlyなxhtmlの記述ができる
やりかた
- xhtmlで 関連付け対象のIDを引数としてスタイルを返却する
イメージは、
JSFでエラーのある項目の背景色を変える - じゃばらの手記
に近しいですが、異なるのは component.clientId
ではなく、通常のIDを指定するところです。
ここまでに、通常のIDとJSFのClinetIdを関連付ける仕組みを実装済みなので、それを使います。
実装
xhtmlから使用するスタイル制御を行うクラス
BeanValidationExceptionのInterceptorで
を取得しているので、それらをマージして目的の判定を行う情報を編集します。
@Action @Interceptor @Priority(Interceptor.Priority.APPLICATION) @Dependent public class BeanValidationExceptionInterceptor { private final CurrentViewContext context; private final MessageConverter messageConverter; private final MessageWriter messageWriter; private final ClientComplementManager clientComplementManager; private final ErrorStyle errorStyle; @Inject public BeanValidationExceptionInterceptor(CurrentViewContext context, MessageConverter messageConverter, MessageWriter messageWriter, ClientComplementManager clientComplementManager, ErrorStyle errorStyle) { this.context = context; this.messageConverter = messageConverter; this.messageWriter = messageWriter; this.clientComplementManager = clientComplementManager; this.errorStyle = errorStyle; } @AroundInvoke public Object invoke(InvocationContext ic) throws Exception { String currentViewId = context.currentViewId(); try { return ic.proceed(); } catch (BeanValidationException ex) { ClientIdsWithComponents clientIdsWithInputComponents = new InputComponentScanner().scan(); ClientIdMessages clientidMessages = messageConverter.toClientIdMessages(ex.getValidatedResults(), ic.getTarget().getClass().getSuperclass(), clientIdsWithInputComponents); ClientIdsWithComponents clientIdsWithHtmlMessages = new HtmlMessageScanner().scan(); messageWriter.appendErrorMessageToComponent(clientidMessages.toClientIdMessagesForWriting(clientIdsWithHtmlMessages)); FacesContext.getCurrentInstance().validationFailed(); clientComplementManager.setClientidMessages(clientidMessages); //ここで、エラースタイルを管理するクラスへ情報を渡す this.errorStyle.set(clientIdsWithInputComponents, clientidMessages); return currentViewId; } } }
実際に状態を保持するクラス
@Named @RequestScoped public class ErrorStyle { private ClientIdsWithComponents clientIdsWithComponents; private String errorStyle; @PostConstruct private void init() { this.errorStyle = "error"; this.clientIdsWithComponents = new ClientIdsWithComponents(); } public void set(ClientIdsWithComponents clientIdsWithInputComponents, ClientIdMessages clientidMessages) { Set<String> clientIds = clientidMessages.getList().stream() .map(ClientIdMessage::getClientId) .collect(Collectors.toSet()); this.clientIdsWithComponents = clientIdsWithInputComponents.filter(clientIds); } /** * 指定したIDの項目が検証不正だった場合に適用する styleClass を返却します. * <P> * xhtmlでのパラメータ指定時には、シングルクウォートで値を指定してください. * * @param id 対象となるコンポーネントのID(JSFのクライアントIDではありません) * @return 当該項目IDにエラーがない場合は 空文字を返却します. */ public String byId(String id) { return this.clientIdsWithComponents.getOrNull(id) == null ? "" : errorStyle; } }
xhtml側での記述
エラーになった際に関連付けさせてCSSクラス文字列を返したいところに EL式を記述します。 ポイントは xhtmlのIdを そのまま使っているところです。
また、jsf:styleClass
で記述していた箇所を class
とすることで、HTML Friendlyな記述が出来ました。
これで デザイナーとの協業も実現できますね。
気をつけるのは、Idの指定は シングルクウォートで囲むというところです。*1
<div class="field #{errorStyle.byId('email')}"> <label>利用者ID</label> <input class="short-input" type="text" placeholder="someone@example.com" jsf:id="email" jsf:value="#{userRegistrationPage.email}"/> <h:message for="email" styleClass="error-message ui left pointing red basic label" /> </div>
Code
vermeer_etc / jsf-ddd / source / — Bitbucket
さいごに
結局のところ、xhtmlで記述したIDと JSFが導出するIDとの関連付けを どうにかすれば色んなことが出来るんですよね。
私は自作をしましたが*2
JSFのオリジナルの部品として、そういうのを扱うAPIなりクラスがあれば もっと使いやすくなるんじゃないでしょうかねぇ。*3