Salut à tous,
j'aimerais connaitre la meilleur facon de créer nos propres composants ainsi de faire une template en jsf(j'ai entendu parler de facelets,pas trop compris le systéme,si vous connaissez un site si possible en fr,ca serait sympa)
Version imprimable
Salut à tous,
j'aimerais connaitre la meilleur facon de créer nos propres composants ainsi de faire une template en jsf(j'ai entendu parler de facelets,pas trop compris le systéme,si vous connaissez un site si possible en fr,ca serait sympa)
Bonjour,
Il faudrait en savoir un peu plus sur le genre de composant que tu veux créer.
En gros, il y a deux voies :
1. Tu utilises les Facelets. Il s'agit là de faire un composant représentant une composition (un ensemble) de composants déjà existants. C'est rapide, simple, mais limité : par exemple, il n'est pas forcément possible d'affecter un ID à un composant de ce type.
Voici un composant fait à partir de composition qui permet de gérer facilement une colonne d'une DataTable (avec des liens permettant de trier la colonne en question) :
(j'ai un peu simplifié le code, voir la page ici pour plus de détails).Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" ...> <ui:composition> <h:column> <f:facet name="header"> <h:panelGroup> ${label} [ <!-- Sort ascending --> <h:commandLink styleClass="smallLink" action="#{backingBean.sort}"> <h:outputText value="asc" /> <f:param name="by" value="${fieldName}"/> <f:param name="order" value="asc"/> </h:commandLink> , <!-- Sort descending --> <h:commandLink styleClass="smallLink" action="#{backingBean.sort}"> <h:outputText value="asc" /> <f:param name="by" value="${fieldName}"/> <f:param name="order" value="dec"/> </h:commandLink> ] </h:panelGroup> </f:facet> <!-- Display the field name --> <h:outputText value="${entity[fieldName]}"/> </h:column> </ui:composition> </html>
2. La seconde voie est de créer un composant en les codant en Java. Là, c'est plus complexe (il faut créer une classe pour le Renderer, une classe pour l'objet JSF, etc.) mais ça permet de créer absolument tout ce que l'on veut.
De mon côté, sur mon projet, nous utilisons les 2 méthodes. Quant la composition est possible, on l'utilise, sinon on crée nos propres composants. En fait, pour être plus précis, nous créons des composants en étendant des composants déjà existants.
Par exemple, nous avions besoin de créer un composant de type AjaxButton (a4j:commandButton), nous avons donc créer notre composant (qui étend la classe org.ajax4jsf.ajax.html.HtmlAjaxCommandButton), ainsi que le composant graphique (étandant la classe org.ajax4jsf.renderkit.html.CommandButtonRenderer).
J'espère t'avoir mis sur la bonne voie...
merci pour ta réponse :D
Ceux que j'ai compris par rapport à ton exemple,c'est qu'une variable contient le code permettant dans ton cas de trier une datable
Et si par exemple,j'ai envi de créer des composants dynamiques à partir d'un fichier xml.Je m'explique,j'ai un fichier qui contient des informations(faut voir sous qu'elle forme)et à partir de ses données,nous pouvons créer par exemple une datatable,un champ texte...,en fait des composant dynamique.
Aurais tu un petit exemple pour avoir un appercu d'un composant créer totalement
De rien :)
Tu parles de quoi au juste ? De ça : action="#{backingBean.sort}" ?
Ce bout de code signifie juste que lorsque l'utilisateur clique sur le lien, alors c'est la méthode sort du bean backingBean qui sera exécutée.
Il s'agit juste d'une expression EL.
Le principe général d'une composition, c'est d'externaliser un ensemble (une composition) de composants déjà existants, et ensuite de les appeler dans d'autres pages. Par exemple, l'exemple donné sera appelé juste par :
Hum, c'est bizarre comme idée. Pourquoi faire cela ? J'ai un peu de mal à comprendre pourquoi par moment tu devrais afficher un datatable, d'autre fois un champ texte... Pourquoi ne pas faire des sous-pages qui seront affichées ou non selon certaines conditions.Code:
1
2
3
4 ... <myns:myColumnComponent label="Item description" fieldName="description" entity="#{myBean}"/> ...
Tu peux jouer avec l'attribut rendered pour t'aider. Un exemple:
Ce code dit ceci : Si l'attribut readOnly (getter isReadOnly du bean myBean) vaut true, alors c'est le composant outputText qui sera affiché. S'il faut false, alors ce sera le composant inputText qui sera affiché. Mais jamais les deux en même temps.Code:
1
2
3
4
5 ... <h:inputText value="" ... rendered="#{!myBean.isReadOnly}"/> <h:outputText value="#{myBean.value}" ... rendered="#{myBean.isReadOnly}"/> ...
Voici un exemple de composant créé en Java. En fait, de mon côté, je ne fais qu'étendre un composant qui existe déjà. Ca me facilite la tâche. Et généralement, les composants dont j'ai besoin ne sont que des composants déjà existants, mais avec un certain nombre de nouvelles fonctionnalités, ou alors avec un rendu différent.
Mon exemple ici c'est la possibilité de créer un bouton avec des bords arrondis . Du coup, j'étends le <h:commandButton>, mais je change un peu son rendu, pour construire un petit tableau dans lequel je mettrais mes images pour créer le rendu du bouton.
Voici l'objet Java:
La classe qui se charge de son rendu:Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 package mypackage.component; import javax.faces.component.html.HtmlCommandButton; import javax.faces.context.FacesContext; import javax.faces.el.ValueBinding; public class MyHtmlButton extends HtmlCommandButton { private String buttonType; public String getButtonType() { if (buttonType != null) { return buttonType; } ValueBinding vb = getValueBinding("buttonType"); if (vb != null) { return (String) vb.getValue(getFacesContext()); } return null; } public void setButtonType(String buttonType) { this.buttonType = buttonType; } @Override public void restoreState(FacesContext context, Object state) { Object values[] = (Object[]) (Object[]) state; super.restoreState(context, values[0]); buttonType = (String) values[1]; } @Override public Object saveState(FacesContext context) { Object[] values = new Object[2]; values[0] = super.saveState(context); values[1] = buttonType; return (Object) values; } }
Ensuite, je me créé un fichier taglib (romain.taglib.xml) :Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 package mypackage.renderkit; import java.io.IOException; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import mypackage.MyComponentUtils; import com.sun.faces.renderkit.html_basic.ButtonRenderer; public class MyButtonRenderer extends ButtonRenderer { public OpmButtonRenderer() { super(); } @Override public void encodeBegin(FacesContext context, UIComponent component) throws IOException { ResponseWriter writer = context.getResponseWriter(); String buttonType = getButtonType(component); writer.startElement("table", null); writer.writeAttribute("border", "0", "table"); writer.writeAttribute("cellpadding", "0", "table"); writer.writeAttribute("cellspacing", "0", "table"); writer.startElement("tr", null); writer.startElement("td", null); writer.startElement("img", null); writer.writeAttribute("src", MyComponentUtils.getResourceUrl("images/button_" + buttonType + "_left.gif"), "img"); writer.endElement("img"); writer.endElement("td"); writer.startElement("td", null); component.getAttributes().put("styleClass", buttonType + "Button"); super.encodeBegin(context, component); } @Override public void encodeEnd(FacesContext context, UIComponent component) throws IOException { super.encodeEnd(context, component); ResponseWriter writer = context.getResponseWriter(); writer.endElement("td"); writer.startElement("td", null); writer.startElement("img", null); writer.writeAttribute("src", MyComponentUtils.getResourceUrl("images/button_" + getButtonType(component) + "_right.gif"), "img"); writer.endElement("img"); writer.endElement("td"); writer.endElement("tr"); writer.endElement("table"); } private String getButtonType(UIComponent component) { String type = (String) component.getAttributes().get("buttonType"); if ((type != null) && (type.equalsIgnoreCase("std") || type.equalsIgnoreCase("default"))) { return type.toLowerCase(); } return MyComponentUtils.DEFAULT_BUTTON_CSS_CLASS; } }
Je "lie" ces types aux classes Java dans le fichier faces-config.xml:Code:
1
2
3
4
5
6
7
8
9
10
11
12<facelet-taglib> <namespace>http://romain/jsf</namespace> ... <tag> <tag-name>button</tag-name> <component> <component-type>my.component.button</component-type> <renderer-type>my.renderkit.button</renderer-type> </component> </tag> </facelet-taglib>
Maintenant, quand je veux appeler ce composant dans une page, je fais :Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 <faces-config> ... <component> <component-type>my.component.button</component-type> <component-class>mypackage.MyHtmlButton</component-class> </component> <render-kit> <render-kit-id>HTML_BASIC</render-kit-id> <renderer> <component-family>javax.faces.Command</component-family> <renderer-type>my.renderkit.button</renderer-type> <renderer-class>mypackage.MyButtonRenderer</renderer-class> </renderer> </render-kit> </faces-config>
J'espère être clair :o)Code:
1
2
3
4
5
6
7
8
9 <a4j:page xmlns="http://www.w3.org/1999/xhtml" ... xmlns:romain="http://romain/jsf"> ... <romain:button .../> ... </a4j:page>
niquel :king:
Lors de l'appel de ce composant,je ne vois pas trop les tags à mettre et que rajouter à la pace des ...
Dans mon cas, j'ai étendu le composant <h:commandButton/>.
J'ai donc à ma disposition toute la liste des attributs de ce composant. Tu peux les voir ici.
J'ai également ajouté à mon composant un attribut "buttonType", qui me permet en fait de définir la classe CSS qui sera utilisée pour le bouton.
'essaye de reproduire ton exemple,lors du fichier MyButtonRendered,il me manque le fichier MyComponentUtils
Oui, en effet. Il s'agit juste d'une classe d'utilitaires, dont voici le code :
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 package mypackage; import javax.el.ValueExpression; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; public final class MyComponentUtils { private OpmComponentUtils() { } public static final String DEFAULT_BUTTON_CSS_CLASS = "std"; /** * Returns the complete Url of a resource handled by the Weblet framework. * * @param resource The resource we are looking for. * @return The complete Url of the resource. */ public static String getResourceUrl(String resource) { StringBuffer sb = new StringBuffer(); sb.append(FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath()); if (!resource.startsWith("/")) { sb.append("/"); } sb.append(resource); return sb.toString(); } /** * Search for the value of an attribute. * * @param context The Faces Context. * @param component The component on which the attribute has been set. * @param attributeName The name of the Attribute. * @return The value of the attribute, may be <code>null</code>. */ public static Object getAttributeValue(FacesContext context, UIComponent component, String attributeName) { Object value = component.getAttributes().get(attributeName); if (value != null) { return value; } // If value is null, this may indicates that attribute value is given as an EL expression. ValueExpression ve = component.getValueExpression(attributeName); if (ve != null) { return ve.getValue(context.getELContext()); } return null; } }
toujours des erreurs
Code:
1
2
3
4
5 public class MyButtonRenderer extends ButtonRenderer { public OpmButtonRenderer() { super(); }
Je ne vois pas du tout comment appeller le composant dans la pageCode:
1
2
3
4
5
6
7
8
9 public final class MyComponentUtils { private OpmComponentUtils() { } ValueExpression ve = component.getValueExpression(attributeName); if (ve != null) { return ve.getValue(context.getELContext());
Code:
1
2
3
4 <a4j:page xlmns="http://www.3.org/1999/xhtml" xmlns:stephane="http://stephane/jsf"> <stephane:button/> </a4j:page>
Des erreurs ? C'est qu'il te manque les bonnes libraires JSF alors...
Dans ce code, tu "appelles" le composant. Il s'agit là d'un exemple d'une page écrite en XHTML. Ce serait grosso modo la même chose avec une page JSP.
Le fait d'écrire <stephane:button .../> dans cette page indique à JSF que lorsqu'il créera la page HTML, il créera un composant de type Button. C'est son renderer (la classe MyButtonRenderer) qui se chargera d'écrire le code HTML correspondant au composant.
Est-ce plus clair ?
yes merci,pour mes erreurs pourtant j'utilise les lib jsf1.2 et uicomponent est bien présent mais n'a pas la méthode getValueExpression
Si tu as un souci avec getValueExpression, supprime la méthode getAttributeValue() de ma classe MyComponentUtils. Elle n'est pas utile dans mon exemple.
Edit : Pourtant, la méthode getValueExpression() existe bien dans UIComponent depuis la version 1.2 de JSF (voir ici).
Dans ton classpath, peut-être y a-t'il une librairie contenant une version plus ancienne de UIComponent. Ce qui expliquerait qu'il ne réussisse pas à trouver cette méthode.
ok,mais je suis navré je ne vois pas du tout comment appeller mon composant
Il me met impossible de trouver l'uri http://stephane/jsfCode:
1
2
3 <%@taglib uri="http://stephane/jsf" prefix="comp"%> <comp:button value="coucou"/>
J'ai essayé d'aprés ta balise <a4ij met je ne la posséde pas,j'ai vu que c'est du ajax,si j'ai envi d'appeller uniquement ce composent,comment dois je procéder?
Te fille tout ma partie de mon code permettant de créer le composant
pagination.jsp
Faces-config.xmlCode:
1
2
3 <%@taglib uri="/WEB-INF/stephane.taglib.tld" prefix="d"%> <d:button value="coucou"/>
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 <faces-config> <component> <component-type>buttonType</component-type> <component-class>composant.MyHtmlButton</component-class> </component> <render-kit> <render-kit-id>HTML_BASIC</render-kit-id> <renderer> <component-family>javax.faces.Command</component-family> <renderer-type>my.renderkit.button</renderer-type> <renderer-class>composant.MyButtonRenderer</renderer-class> </renderer> </render-kit>
stephane.taglib.tld
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <taglib> <tlib-version>1.0</tlib-version> <jsp-version>1.2</jsp-version> <short-name>d</short-name> <uri>/WEB-INF/stephane.taglib.tld</uri> <tag> <name>buttonType</name> <tag-class>composant.MyButtonRenderer</tag-class> <body-content>JSP</body-content> </tag> </taglib>
MyButtonRenderer .java
MyHtmlButtonCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 package composant; import java.io.IOException; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import com.sun.faces.renderkit.html_basic.ButtonRenderer; public class MyButtonRenderer extends ButtonRenderer { public void encodeBegin(FacesContext context, UIComponent component) throws IOException { ResponseWriter writer = context.getResponseWriter(); String buttonType = getButtonType(component); writer.startElement("table", null); writer.writeAttribute("border", "0", "table"); writer.writeAttribute("cellpadding", "0", "table"); writer.writeAttribute("cellspacing", "0", "table"); writer.startElement("tr", null); writer.startElement("td", null); writer.startElement("img", null); writer.writeAttribute("src", MyComponentUtils.getResourceUrl("images/button_" + buttonType + "_left.gif"), "img"); writer.endElement("img"); writer.endElement("td"); writer.startElement("td", null); component.getAttributes().put("styleClass", buttonType + "Button"); super.encodeBegin(context, component); } public void encodeEnd(FacesContext context, UIComponent component) throws IOException { super.encodeEnd(context, component); ResponseWriter writer = context.getResponseWriter(); writer.endElement("td"); writer.startElement("td", null); writer.startElement("img", null); writer.writeAttribute("src", MyComponentUtils.getResourceUrl("images/button_" + getButtonType(component) + "_right.gif"), "img"); writer.endElement("img"); writer.endElement("td"); writer.endElement("tr"); writer.endElement("table"); } private String getButtonType(UIComponent component) { String type = (String) component.getAttributes().get("buttonType"); if ((type != null) && (type.equalsIgnoreCase("std") || type.equalsIgnoreCase("default"))) { return type.toLowerCase(); } return MyComponentUtils.DEFAULT_BUTTON_CSS_CLASS; } }
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 package composant; import javax.faces.component.html.HtmlCommandButton; import javax.faces.context.FacesContext; import javax.faces.el.ValueBinding; public class MyHtmlButton extends HtmlCommandButton{ private String buttonType; public String getButtonType() { if(buttonType!=null){ return buttonType; } ValueBinding vb=getValueBinding("buttonType"); if(vb!=null){ return(String)vb.getValue(getFacesContext()); } return null; } public void setButtonType(String buttonType) { this.buttonType = buttonType; } public void restoreState(FacesContext context,Object state){ Object values[]=(Object[])(Object[])state; super.restoreState(context,values[0]); buttonType=(String)values[1]; } public Object saveState(FacesContext context){ Object[] values=new Object[2]; values[0]=super.saveState(context); values[1]=buttonType; return (Object)values; } }
Je suppose étant donné qu'il connait le tag,que c'est un probléme vennant plutot de MyHtmlButton,mauvais lien....
Regarde please si ce sont les bons liens dans le fichier tld et xml
Je ne sais pas si ton problème vient de là, mais de mon exemple, je définis les composants dans un facelet-taglib, c'est-à-dire un fichier blabla.taglib.xml, tandis que toi, tu as opté pour un fichier taglib (blabla.tld).
je sais mais j'ai essayé directement ta facon,marche pas non plus,et comme j'ai vu sur un site que c'est un fichier tld,j'ai tenté le coup
Une petite question,comment il peut savoir c'est qu'elle fichier xml qui contient le composant créer(stephane.taglib.xml)
Sinon,si t'aurais deja un petit projet simple que je puisse comprendre comprennant biensur la création d'un composant si possible un bouton arrondie,car il faut que j'en insére un à mon projet,ca serait :yaisse2:
Si je ne dis pas de bétise, JSF (facelets) va charger tous les les fichiers *taglib.xml qu'il trouve dans le classpath.
Chacun de ces fichier à un namespace particulier :
(le namespace ne correspond à rien en soit, ce n'est pas vraiment une URL, mais plutôt un identifiant)Code:
1
2
3
4<facelet-taglib> <namespace>http://bla.bla.bla/jsf</namespace> ...
Ensuite, dans le fichier JSP ou XHTML, il faut indiquer que tu utilises ce namespace en précisant quel préfix tu utilises pour les composants sur ta page :
ouCode:
1
2 <%@taglib uri="http://bla.bla.bla/jsf" prefix="bla" %>
Enfin, tu n'as plus qu'à utiliser le composant en le préfixant correctement :Code:
1
2
3<h:page xmlns:bla="http://bla.bla.bla/jsf"> ...
Code:
1
2 <bla:monComposant ...>
en mettant n'importe qu'elle nom,je ne vois plus le tag
dans l'uri j ai mis http;//stephane.jsf
Et dans la page jsp,lorsque je fais le <prefix:,cette fois ci il trouve quedal tandis que si je mets le chemin vers le fichier xml,i trouve bien mais bon,dans les deux cas,ca ne marchent pas :roll: