Custom Facelet component in JSF - jsf

Is it possible to create a custom JSF core Facelet component. Something like <custom:composition> of <ui:composition>, or <custom:include> for <ui:include>
It would be helpful if someone can tell me the steps involved.
Thanks in advance,
Kaushal

It are in essence taghandlers. I.e. classes extending from TagHandler.
Here's a Hello World taghandler.
com.example.HelloTagHandler
public class HelloTagHandler extends TagHandler {
public HelloTagHandler(TagConfig config) {
super(config);
}
#Override
public void apply(FaceletContext context, UIComponent parent) throws IOException {
// Do your job here. This example dynamically adds another component to the parent.
if (ComponentHandler.isNew(parent)) {
UIOutput child = new HtmlOutputText();
child.setValue("Hello World");
parent.getChildren().add(child);
}
nextHandler.apply(context, parent); // Delegate job further to first next tag in tree hierarchy.
}
}
/WEB-INF/my.taglib.xml
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
version="2.0"
>
<namespace>http://example.com/my</namespace>
<tag>
<tag-name>hello</tag-name>
<handler-class>com.example.HelloTagHandler</handler-class>
</tag>
</facelet-taglib>
/WEB-INF/web.xml (note: this part is not mandatory when my.taglib.xml is in /META-INF folder of a JAR file inside /WEB-INF/lib like as with JSF component libraries):
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/my.taglib.xml</param-value>
</context-param>
Usage in /some.xhtml
<html ... xmlns:my="http://example.com/my">
...
<my:hello />
To see the source code of Mojarra implementation of <ui:composition> and <ui:include>, click the links.
See also:
When to use <ui:include>, tag files, composite components and/or custom components?

Related

JSF language not changing [duplicate]

This question already has answers here:
creating a simple link that invokes a jsf method
(1 answer)
Localization in JSF, how to remember selected locale per session instead of per request/view
(5 answers)
Closed 5 years ago.
I was trying to include translations in my website, german and english. Sadly i seem to be to stupid to do it right and can't spot the problem. Can someone tell my why my language wont load? It always sticks with the default language.properties file and wont change to language_de / language_en
Here is my faces-config
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
version="2.2">
<application>
<locale-config>
<default-locale>de</default-locale>
<supported-locale>en</supported-locale>
</locale-config>
<resource-bundle>
<base-name>language</base-name>
<var>language</var>
</resource-bundle>
</application>
</faces-config>
Here is my project structure
Here is the component i use to change the language
<p:link id="buttonHeader_languageLink">
<p:graphicImage id="buttonHeader_languageIcon"
value="#{languageBean.currentLanguage.graphicPath}"
onclick="#{languageBean.changeLanguage()}"></p:graphicImage>
</p:link>
and my ManagedBean
#ManagedBean
#SessionScoped
public class LanguageBean implements Serializable {
private static final long serialVersionUID = 1L;
private final static SessionLanguage GERMAN = new SessionLanguage(new Locale("de"), "/resource/image/german.png");
private final static SessionLanguage ENGLISH = new SessionLanguage(new Locale("en"), "/resource/image/english.png");
private SessionLanguage currentLanguage = GERMAN;
public SessionLanguage getCurrentLanguage() {
return currentLanguage;
}
public void changeLanguage() {
if(currentLanguage.equals(GERMAN)){
currentLanguage = ENGLISH;
}else{
currentLanguage = GERMAN;
}
FacesContext.getCurrentInstance().getViewRoot().setLocale(currentLanguage.getLocale());
}
}
And this is the response header after clicking the link which seems to be missing the language :/

Creating a taglib.xml with a TagHandler makes annotated custom components fail to work

As an exercise, I'm creating some custom components in jsf 2.2 by using just annotations. For now I am not interested in a taglib for completion in the ui and since that exempts me from maintaining it, initial development is quicker. This al works perfectly. In this example I have one component that, for now, extends the PrimeFaces InputText (changing that does not make a difference), a clean faces-config (correctly 2.2 namespaced) and a simple page
component:
package my.custom.xforms;
import javax.faces.application.ResourceDependencies;
import javax.faces.application.ResourceDependency;
import javax.faces.component.FacesComponent;
import org.primefaces.component.inputtext.InputText;
#FacesComponent(value = "xforms.input", createTag = true,
namespace = "http://www.w3.org/2002/xforms", tagName = "input")
#ResourceDependencies({
#ResourceDependency(library="primefaces", name="primefaces.css"),
#ResourceDependency(library="primefaces", name="jquery/jquery.js"),
#ResourceDependency(library="primefaces", name="primefaces.js")}
)
public class Input extends InputText {
public Input() {
setRendererType("xforms.inputRenderer");
}
#Override
public String getFamily() {
return "my.xforms.components";
}
}
faces-config.xml
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
version="2.2">
</faces-config>
page
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:p="http://primefaces.org/ui">
<h:head/>
<h:body>
<xf:input />
</h:body>
</html>
This nicely shows a 'PrimeFaces' text input.
As an exercise, I added a tag that defines a model (internal to 'my' engine) that has no ui interaction. So my thought was to add a TagHandler (it does not need to manipulate the tags before it either, so maybe I should just extend UIComponentBase, but that is not the 'issue' now). TagHandlers cannot be created via annotations from what I can see, so I created a taglib.xml and put just my TagHandler in there.
taghandler
package my.custom.xforms;
import java.io.IOException;
import javax.el.ELException;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.view.facelets.ComponentHandler;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.FaceletException;
import javax.faces.view.facelets.TagConfig;
import javax.faces.view.facelets.TagHandler;
public class ModelTagHandler extends TagHandler {
public ModelTagHandler(TagConfig tagConfig) {
super(tagConfig);
}
public void apply(FaceletContext faceletContext, UIComponent parent) throws IOException, FacesException, FaceletException, ELException {
if (ComponentHandler.isNew(parent)) {
System.out.println("XForms Model encountered");
}
}
}
new page
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:p="http://primefaces.org/ui">
<h:head/>
<h:body>
<xf:model /> <!-- can be put in h:head to, does not make a difference -->
<xf:input />
</h:body>
</html>
taglib
<facelet-taglib version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facelettaglibrary_2_2.xsd">
<namespace>http://www.w3.org/2002/xforms</namespace>
<tag>
<tag-name>model</tag-name>
<handler-class>my.custom.xforms.ModelTagHandler</handler-class>
</tag>
</facelet-taglib>
To my surprise (well only partly), my custom component created with just annotations, stopped working with the following error.
/components/page.xhtml #12,33 <xf:input> Tag Library supports namespace: http://www.w3.org/2002/xforms, but no tag was defined for name: input
It only started working again if I put the custom component in my taglib to.
<tag>
<tag-name>input</tag-name>
<component>
<component-type>xforms.input</component-type>
<renderer-type>xforms.inputRenderer</renderer-type>
</component>
</tag>
Is this expected behaviour? Or should the taghandler be declared in a different way? I tried a lot of combinations of keywords in google, but to no avail. Not finding a bug, nor any hint to do things differently, nothing.
I'm currently running all this in
Wildfly 8.0.0-Final (Mojarra 2.2.5-jbossorg-3 20140128-1641)
java7
PrimeFaces 5.1
OmniFaces 2.0.
I will try to run it on a newer WildFly tomorrow or just try to update to the latest Mojarra (maybe in a clean Tomcat or whatever).

JSF 1.2 custom component does not render/encode children

I have two custom components:
CustomUIComponent extends UIComponentBase
CustomChildUIComponent extends UIComponentBase
In CustomUIComponent I implement encodeBegin, encodeChildren and encodeEnd - in encodeChildren I set some custom attribute to be forwarded to the child component.
In CustomChildUIComponent I implement only encodeBegin.
In addition to these classes I added the components in the faces-config.xml:
<component>
<component-type>test.JsfMessage</component-type>
<component-class>test.CustomUIComponent</component-class>
</component>
<component>
<component-type>test.JsfChildMessage</component-type>
<component-class>test.CustomChildUIComponent</component-class>
</component>
And I have the custom taglib.xml configured in the web.xml and contains:
<tag>
<tag-name>customMessage</tag-name>
<component>
<component-type>test.JsfMessage</component-type>
</component>
</tag>
<tag>
<tag-name>customChildMessage</tag-name>
<component>
<component-type>test.JsfChildMessage</component-type>
</component>
</tag>
Finally in my Facelets page I am trying to execute:
<myns:customMessage message="Hello World!!!" var="mytestvar">
<myns:customChildMessage partnermsg="#{mytestvar}" />
</myns:customMessage>
The result is that the parent is rendered but the child component does not.
Am I doing something wrong?
I tried checking the super.encodeChildren but it checks:
Renderer renderer = getRenderer(context);
if(renderer != null) ...
I am not using a renderer class, but as I understand it is not a must.
The encodeChildren() method of your custom component will only be called if the getRendersChildren() method of the very same custom component returns true. This is specified in the javadoc:
This method will only be called if the rendersChildren property is true.
So make sure that you've overridden that accordingly, it namely defaults to false:
#Override
public boolean getRendersChildren() {
return true;
}

JSF 1.2 facelets custom component

I am trying to develop custom control in JSF 1.2 (using facelets).
I followed steps from different tutorials (defining .tld, taglib.xml, registered component in faces-config.xml and implementing UIComponent (component renders itself) and UIComponentELTag classes) and my component is rendered, I have value bound to it, but attributes I defined for that tag are ignored. I logged various methods in Tag class and noticed that none of the methods is ever called.
What am I missing? Is there a reason Tag handler class is never invoked?
Thanks in advance.
My taglib.xml file is:
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"java.sun.com/dtd/facelet-taglib_1_0.dtd">
<facelet-taglib>
<namespace>dynamissoft.com/entities/ui</namespace>
<tag>
<tag-name>legalEntityView</tag-name>
<component>
<component-type>rs.bozic.wastemanager.LegalEntityView</component-type>
</component>
</tag>
</facelet-taglib>
Have you tried creating a custom component using facelets (xml only). That's the most easy way, using facelets, usually, the different java classes aren't needed anymore.
Very rough overview:
Create facelet xml file (like myComponent.xhtml)
Register in the facelet inside a taglib (which in turn should be defined in the web.xml)
Optionally, create some support beans in Java
You can pass values/beans to your component using normal tag paramets:
Using the component
Inside the component
Param1 is just printed: #{myParam2}
Param2 used as value for table
...
There are excellent tutorials on Google, like the one from IBM.
If possible, consider using JSF 2.0. Facelets are integrated, and you have more flexibility to create your custom components. I created a blog posting a while ago on that: http://blog.whitehorses.nl/2010/02/08/jsf-2-0/ (or Google yourself)
Just to expand Gerbrand's answer a bit, here's a procedure for a simple Facelets-compatible component. It renders a span tag that wraps a text specified via component's text attribute.
First create the component class (in our case it's just a flavour of
UIOutput):
package sample.mytag;
import java.io.IOException;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
public class SpanComponent extends UIOutput{
private String text;
#Override
public Object saveState(FacesContext context) {
Object values[] = new Object[2];
values[0] = super.saveState(context);
values[1] = target;
return ((Object) (values));
}
#Override
public void restoreState(FacesContext context, Object state) {
Object values[] = (Object[])state;
super.restoreState(context, values[0]);
target = (String)values[1];
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
ResponseWriter writer=context.getResponseWriter();
writer.startElement("span", component);
writer.writeAttribute("id", id, null);
writer.writeText(text, null);
writer.endElement("span");
writer.flush();
}
#Override
public String getFamily(){
return "myTag.component";
}
#Override
public void encodeEnd(FacesContext context) throws IOException {
return;
}
#Override
public void decode(FacesContext context) {
return;
}
}
Next, we need a taglib XML file, let's call it mytag.taglib.xml and put it inside WEB-INF dir.
<!DOCTYPE facelet-taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"WEB-INF/facelet-taglib_1_0.dtd">
<facelet-taglib>
<namespace>http://sample.tag/mytags</namespace>
<tag>
<tag-name>myspan</tag-name>
<component>
<component-type>myTag.component</component-type>
</component>
</tag>
</facelet-taglib>
Note that:
.taglib.xml suffix is mandatory
<component-type> should have the same
value that is returned by component's getFamily() method
you can
replace WEB-INF/facelet-taglib_1_0.dtd with
http://java.sun.com/dtd/facelet-taglib_1_0.dtd
It's time to modify web.xml and faces-config.xml.
Former should be modified with
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>/WEB-INF/mytag.taglib.xml</param-value>
</context-param>
faces-config.xml should get
<component>
<component-type>myTag.component</component-type>
<component-class>sample.mytag.LabelComponent</component-class>
</component>
We're good to go!
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:sample="http://sample.tag/mytag">
<sample:myspan text="I'm inside a span!"/>
</ui:composition>

Facelets and JSTL (Converting a Date to a String for use in a field)

I need to convert a Date to a String within a page (I dont want to add loads of toStrings to my domain model so adding to the bean is not an option).
<ice:graphicImage value="bean.image" title="#{bean.date}"/>
The above code works but formats the Date in the default format...I would like to change the format.
I have tried using JSTL fmt but this doesnt seem to be compatible with Facelets JSF Convert dates for title attribute . Is there a workaround for this (doesnt have to use JSTL)?
Thanks.
Indeed, you cannot use the "good old" JSTL in Facelets anymore the way as you would do in JSP. Facelets only supports a limited subset of JSTL (and has it already builtin, the JSTL JAR file is in fact superfluous).
You're forced to write a custom tag or better, a custom EL function, for this purpose.
Let's assume that we want to be able to do this:
<ice:graphicImage ... title="#{fmt:formatDate(bean.date, 'yyyy-MM-dd')}" />
Roughly said thus the same what the JSTL <fmt:formatDate> tag can do, but then in flavor of an EL function so that you can use it everywhere without the need for an "intermediating" tag. We want it to take 2 arguments, a Date and a SimpleDateFormat pattern. We want it to return the formatted date based on the given pattern.
First create a final class with a public static method which does exactly that:
package com.example.el;
import java.text.SimpleDateFormat;
import java.util.Date;
public final class Formatter {
private Formatter() {
// Hide constructor.
}
public static String formatDate(Date date, String pattern) {
return new SimpleDateFormat(pattern).format(date);
}
}
Then define it as a facelet-taglib in /META-INF/formatter.taglib.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!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://example.com/el/formatter</namespace>
<function>
<function-name>formatDate</function-name>
<function-class>com.example.el.Formatter</function-class>
<function-signature>String formatDate(java.util.Date, java.lang.String)</function-signature>
</function>
</facelet-taglib>
Then familarize Facelets with the new taglib in the existing /WEB-INF/web.xml:
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>/META-INF/formatter.taglib.xml</param-value>
</context-param>
(note: if you already have the facelets.LIBRARIES definied, then you can just add the new path commaseparated)
Then define it in the Facelets XHTML file as new XML namespace:
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:fmt="http://example.com/el/formatter"
...
>
Finally you can use it as intended:
<ice:graphicImage ... title="#{fmt:formatDate(bean.date, 'yyyy-MM-dd')}" />
Hope this helps.
You can use a converter method in your bean, as:
public class Bean{
...
public String formatDate(Date fecha, String pattern) {
return (new SimpleDateFormat(pattern)).format(fecha);
}
...
}
And, in your page:
<ice:graphicImage value="bean.image" title="#{bean.formatDate(bean.date,'yyyy-MM-dd')}"/>

Resources