2010年7月16日金曜日

faceletsカスタムコンポーネント作成

今回はfaceletsでJSFタグCommandLinkを拡張して部品を作成したいと思います。
※とりあえず"career"という属性を追加したレンダリングを行うのみ
faceletsを使用する場合、独自に作成したJSFカスタムコンポーネントは使用できまんせん。


○やるべきこと
  ・taglibファイルの作成
  ・web.xml登録
  ・カスタムUIコンポーネントクラスの作成
  ・カスタムレンダラーの作成
  ・faces-config.xmlにUIコンポーネントとレンダラーの登録
  ・xhtmlでのカスタムタグ使用


○taglibファイルの作成
 カスタムタグをtaglibファイルを作成してWEB-INF直下に配置します。
 以下を定義します。
  ・namespace
  ・tag-name
  ・component-type
  ・renderer-type
 
○custom.taglib.xml
 <?xml version="1.0"?> 
 <!DOCTYPE facelet-taglib PUBLIC 
   "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" 
   "http://java.sun.com/dtd/facelet-taglib_1_0.dtd"> 
 <facelet-taglib> 
       <namespace>http://www.arc-mind.com/jsf</namespace> 
 
     <tag> 
         <tag-name>extLink</tag-name> 
         <component> 
             <component-type>ui.component.ExtLink</component-type> 
             <renderer-type>ui.render.ExtLinkRender</renderer-type> 
         </component> 
     </tag> 
 
 </facelet-taglib> 


○web.xmlに登録
 web.xmlに作成したcustom.taglib.xmlを登録します。
 web.xmlに以下のcontext-paramを追加。
 ※faceletsのversionによってparam-nameが違うため、以下のどちらかを指定する。
   <context-param> 
        <param-name>javax.faces.FACELETS_LIBRARIES</param-name> 
        <param-value>/WEB-INF/custom.taglib.xml</param-value> 
   </context-param> 

 ※facelets-1.1.15-jsf1.2はこちらの定義で動作
   <context-param> 
         <param-name>facelets.LIBRARIES</param-name> 
         <param-value> 
         /WEB-INF/custom.taglib.xml 
         </param-value> 
  </context-param> 



○カスタムUIコンポーネントクラスの作成
 HtmlCommandLinkクラスを拡張してカスタムUIコンポーネントクラスを作成。
 オーバライドするべきメソッドは以下の通り。
  getFamily()    ファミリ名を返すメソッド
 package ui.component; 
 
 import javax.faces.component.html.HtmlCommandLink; 
 
 public class ExtLink extends HtmlCommandLink { 
 
 
  @Override
  public String getFamily() {
   return "GpsLinkFamily";
  }
 }



○faces-config.xmlにUIコンポーネントとレンダラーの登録
  <component> 
   <component-type>ui.component.ExtLink</component-type> 
   <component-class>ui.component.ExtLink</component-class> 
  </component> 
 
  <render-kit> 
   <render-kit-id>HTML_BASIC</render-kit-id> 
   <renderer> 
    <component-family>ExtLinkFamily</component-family> 
    <renderer-type>ui.render.ExtLinkRender</renderer-type> 
    <renderer-class>ui.render.ExtLinkRender</renderer-class> 
   </renderer> 
  </render-kit> 


○カスタムレンダラーの作成
 必要に応じてdecodeメソッドと各種encodeメソッドをオーバーライド。
 decodeメソッドはmanagedBean適用時(ブラウザ→サーバ)。
 encodeメソッドはレンダリング時(サーバ→ブラウザ)。

 赤字部分のみ追加であとはCommandLinkRendererのコピペ
 encode処理にて属性にcareerを追加してみる。decode処理はそのまま。
 ※ decodeでリクエストパラメータからコンポーネントに設定する場合はsetValue()ではなく、
  setSubmittedValue()で値を設定する。valueChangeListener()がsetValueだと呼ばれないため。
 package ui.render; 
 
 import java.io.IOException; 
 import java.util.Map; 
 
 import javax.faces.component.UIComponent; 
 import javax.faces.component.UIForm; 
 import javax.faces.context.FacesContext; 
 import javax.faces.context.ResponseWriter; 
 
 import com.sun.faces.renderkit.AttributeManager; 
 import com.sun.faces.renderkit.RenderKitUtils; 
 import com.sun.faces.renderkit.html_basic.CommandLinkRenderer; 
 import com.sun.faces.renderkit.html_basic.HtmlBasicRenderer; 
 
 public class ExtLinkRender extends CommandLinkRenderer { 
 
    public ExtLinkRender() 
      { 
     super(); 
      } 
     private static final String ATTRIBUTES[]; 
     private static final String SCRIPT_STATE = "com.sun.faces.scriptState"; 
 
     static 
     { 
         ATTRIBUTES = AttributeManager.getAttributes(com.sun.faces.renderkit.AttributeManager.Key.COMMANDLINK); 
     } 
 
 
  protected void renderAsActive(FacesContext context, UIComponent command) 
    throws IOException { 
   ResponseWriter writer = context.getResponseWriter(); 
   if (writer == null) 
    throw new AssertionError(); 
   String formClientId = getFormClientId(command, context); 
   if (formClientId == null) 
    return; 
   writer.startElement("a", command); 
   writeIdAttributeIfNecessary(context, writer, command); 
   writer.writeAttribute("href", "#", "href"); 
 
   Map attributeMap = command.getAttributes(); 
   writer.writeAttribute("career", "#", null); 
 
   RenderKitUtils.renderPassThruAttributes(writer, command, ATTRIBUTES); 
   RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer, command); 
   String userOnclick = (String) command.getAttributes().get("onclick"); 
   StringBuffer sb = new StringBuffer(128); 
   boolean userSpecifiedOnclick = userOnclick != null 
     && !"".equals(userOnclick); 
   if (userSpecifiedOnclick) { 
    sb.append("var a=function(){"); 
    userOnclick = userOnclick.trim(); 
    sb.append(userOnclick); 
    if (userOnclick.charAt(userOnclick.length() - 1) != ';') 
     sb.append(';'); 
    sb.append("};var b=function(){"); 
   } 
   HtmlBasicRenderer.Param params[] = getParamList(command); 
   String commandClientId = command.getClientId(context); 
   String target = (String) command.getAttributes().get("target"); 
   if (target != null) 
    target = target.trim(); 
   else 
    target = ""; 
   sb.append(getOnClickScript(formClientId, commandClientId, target, 
     params)); 
   if (userSpecifiedOnclick) 
    sb.append("};return (a()==false) ? false : b();"); 
   writer.writeAttribute("onclick", sb.toString(), "onclick"); 
   writeCommonLinkAttributes(writer, command); 
   writeValue(command, writer); 
   writer.flush(); 
  } 
 
  private static boolean hasScriptBeenRendered(FacesContext context) { 
   return context.getExternalContext().getRequestMap().get( 
     com.sun.faces.scriptState) != null; 
  } 
 
  private static void setScriptAsRendered(FacesContext context) { 
   context.getExternalContext().getRequestMap().put( 
     com.sun.faces.scriptState, Boolean.TRUE); 
  } 
 
  private static String getFormClientId(UIComponent component, 
    FacesContext context) { 
   UIForm form = getMyForm(component); 
   if (form != null) 
    return form.getClientId(context); 
   else 
    return null; 
  } 
 
  private static UIForm getMyForm(UIComponent component) { 
   UIComponent parent; 
   for (parent = component.getParent(); parent != null 
     && !(parent instanceof UIForm); parent = parent.getParent()) 
    ; 
   return (UIForm) parent; 
  } 
 
 } 



○xhtmlでのカスタムタグ使用
 taglibファイルに定義したnamespaceでタグを使用する。

<%@ page contentType="text/html;charset=Shift_JIS" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="/WEB-INF/jsf_custum.tld" prefix="custom" %>
<html>
<head>
<title>HelloWorld</title>
</head>
<body>
<f:view>
<h:form>

文字を入力して下さい:<h:inputText id="string" value="#{HelloWorldBean.helloWorld}"/>
<h:commandButton value="送信"/>
<p><h:outputText id="output" value="#{HelloWorldBean.helloWorld}"/></p>


<h:commandButton id="btn1" action="ok" value="遷移" actionListener="#{HelloWorldBean.assembleMessage}" />
<br>
<custom:extlink id="link1" action="ok" actionListener="#{HelloWorldBean.assembleMessage}">カスタムリンク</custom:extlink> 
</h:form>
</f:view>
</body>
</html>




 注意点
  JBossToolでSeemプロジェクトを作成するとsrc/hotとsrc/mainが作成されるが、src/main側に入れないとClassが見つからない
  と怒られる。
   src/hot    エンティティクラス以外のソースディレクトリ
   src/main    エンティティクラスのソースディレクトリ

1 件のコメント:

  1. こんにちは。

    カスタムタグの使い方、勉強になります!

    返信削除