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>
Related
I have following FacesConverter:
#FacesConverter(forClass = Onderwerp.class, managed = true)
public class OnderwerpConverter implements Converter<Onderwerp> {
#Inject
private Web web;
#Override
public Onderwerp getAsObject(FacesContext context, UIComponent component, String value) {
log.trace("Converting to object from string: " + value);
return web.getAllActiveOnderwerpen().stream().filter(o -> o.getId().equals(Long.parseLong(value))).findFirst().get();
}
#Override
public String getAsString(FacesContext context, UIComponent component, Onderwerp onderwerp) {
log.trace("Converting to string from object: " + onderwerp);
return onderwerp.getId().toString();
}
}
The referenced CDI bean is:
#Named
#ApplicationScoped
public class Web { ... }
Faces-config.xml is:
<?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_3.xsd" version="2.3">
Now whatever kind of bean I inject via #Inject it is always null. It seems the injection feature in 2.3 is not working (yet), or I am missing something :)
I'm using Mojarra 2.3.0 on EAP 7.0. Also tested without success using 2.3.3 on EAP 7.0 and 7.1.
My current workaround is replacing the code where I need the injected CDI bean like this:
return CDI.current().select(Web.class).get().getAllActiveOnderwerpen().stream().filter(o -> o.getId().equals(Long.parseLong(value))).findFirst().get();
This works fine, but is kinda ugly of course :)
Anyone has experienced this behavior?
I had the same issue and I foud the solution.
You must create a config bean indicating JSF version:
import javax.faces.annotation.FacesConfig;
import javax.faces.annotation.FacesConfig.Version;
#FacesConfig(
// Activates CDI build-in beans
version=Version.JSF_2_3
)
public class ConfigBean {
}
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?
I asked about pass through attributes in a different question and found I could create a custom renderer for the <p:autocomplete> component but the problem is my custom renderer would be used for every p:autocomplete in my project (site-wide). Therefore I have elected to create a custom component which extends org.primefaces.component.autocomplete.AutoComplete and adds the necessary attributes to the text box.
My initial thought was to add a constructor but it doesn't seem to work because the attribute map is null at this point:
#FacesComponent("com.mycomponents.SiteSearch")
public class SiteSearch extends AutoComplete {
public SiteSearch() {
Map<String,Object> attrs = getAttributes();
attrs.put("x-webkit-speech", null);
attrs.put("x-webkit-grammer", "builtin:search");
attrs.put("onwebkitspeechchange", "this.form.submit();");
attrs.put("placeholder", "Enter a Search Term");
}
}
My other thought was leave this custom component empty (empty class) and then specify a custom renderer that extends org.primefaces.component.autocomplete.AutoCompleteRenderer and modify the attributes there.
After all is said and done, I just need a way to keep these attributes separate to this one text box so just putting a custom renderer on the p:autoComplete is not going to work (unless maybe I can use renderType= attribute for this one p:autoComplete?).
If you need a specific component which uses a different renderer than <p:autoComplete> then you really can't go around creating a custom component with its own family and component type. You can still just extend the PrimeFaces AutoComplete (and its renderer) to save some boilerplate code.
In the custom component, you need to provide getters for those attributes. You could as good specify setters as well, this way you can always override the default values from in the view side. Those getters/setters should in turn delegate to StateHelper.
There's only a little problem with x-webkit-* attributes. The - is an illegal character in Java identifiers. So you have to rename the getters/setters and change the renderer somewhat as the standard renderer relies on the component property name being exactly the same as the tag attribute name. Update: I understand that x-webkit-speech should just be rendered as is (so, no getter/setter necessary) and that x-webkit-grammer is actually a typo, it should be x-webkit-grammar.
Here's how the SiteSearch component can look like:
#FacesComponent(SiteSearch.COMPONENT_TYPE)
public class SiteSearch extends AutoComplete {
public static final String COMPONENT_FAMILY = "com.example";
public static final String COMPONENT_TYPE = "com.example.SiteSearch";
private enum PropertyKeys {
grammar, onspeechchange, placeholder
}
#Override
public String getFamily() {
return COMPONENT_FAMILY;
}
#Override
public String getRendererType() {
return SiteSearchRenderer.RENDERER_TYPE;
}
public String getGrammar() {
return (String) getStateHelper().eval(PropertyKeys.grammar, "builtin:search");
}
public void setGrammar(String grammar) {
getStateHelper().put(PropertyKeys.grammar, grammar);
}
public String getOnspeechchange() {
return (String) getStateHelper().eval(PropertyKeys.onspeechchange, "submit()");
}
public void setOnspeechchange(String onspeechchange) {
getStateHelper().put(PropertyKeys.onspeechchange, onspeechchange);
}
public String getPlaceholder() {
return (String) getStateHelper().eval(PropertyKeys.placeholder, "Enter a Search Term");
}
public void setPlaceholder(String placeholder) {
getStateHelper().put(PropertyKeys.placeholder, placeholder);
}
}
Please note that the getters have all default values specified. If the eval() returns null, then the default value will be returned instead. I have also neutralized the attribute names somewhat so that it can be reused for any future non-webkit browsers by just modifying the renderer accordingly.
And here's how the SiteSearchRenderer renderer should look like for the above component:
#FacesRenderer(
componentFamily=SiteSearch.COMPONENT_FAMILY,
rendererType=SiteSearchRenderer.RENDERER_TYPE
)
public class SiteSearchRenderer extends AutoCompleteRenderer {
public static final String RENDERER_TYPE = "com.example.SiteSearchRenderer";
#Override
protected void renderPassThruAttributes(FacesContext facesContext, UIComponent component, String[] attrs) throws IOException {
ResponseWriter writer = facesContext.getResponseWriter();
writer.writeAttribute("x-webkit-speech", "x-webkit-speech", null);
writer.writeAttribute("x-webkit-grammar", component.getAttributes().get("grammar"), "grammar");
writer.writeAttribute("onwebkitspeechchange", component.getAttributes().get("onspeechchange"), "onspeechchange");
writer.writeAttribute("placeholder", component.getAttributes().get("placeholder"), "placeholder");
super.renderPassThruAttributes(facesContext, component, attrs);
}
}
To use it in the view, we of course need to register it as a tag. Create a /WEB-INF/my.taglib.xml file:
<?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/ui</namespace>
<tag>
<tag-name>siteSearch</tag-name>
<component>
<component-type>com.example.SiteSearch</component-type>
<renderer-type>com.example.SiteSearchRenderer</renderer-type>
</component>
</tag>
</facelet-taglib>
Note that you don't need a <renderer> in faces-config.xml for this anymore. The #FacesRenderer annotation can just do its job on real custom components. So remove the <renderer> entry in faces-config.xml which you created based on your previous question.
Now tell JSF that you've got a custom taglib by the following context param in web.xml:
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/my.taglib.xml</param-value>
</context-param>
Finally you can use it as follows:
<html ... xmlns:my="http://example.com/ui">
...
<my:siteSearch />
You can even specify additional attributes which will override the defaults set in the component:
<my:siteSearch grammar="builtin:language" onspeechchange="alert('peek-a-boo')" placeholder="Search" />
For IDE autocomplete on attributes, you'd need to specify every one as a separate <attribute> in the <tag> declaration in the my.taglib.xml.
I am trying to override renderer for h:selectBooleanCheckbox (for the reasons explained here):
However, I find it impossible to register my renderer. I have tried declaring it in my faces-config.xml:
<render-kit>
<renderer>
<component-family>javax.faces.SelectBoolean</component-family>
<renderer-type>javax.faces.Checkbox</renderer-type>
<renderer-class>com.myapp.CustomCheckboxRenderer</renderer-class>
</renderer>
</render-kit>
The values I grabbed from:
component-family: javax.faces.component.html.HtmlSelectBooleanCheckbox
renderer-type: javax.faces.component.html.SelectBooleanCheckboxTag
But it doesn't work.
I also tried verbosely declaring the RenderKit:
<description>Custom renderers</description>
<render-kit-id>???</render-kit-id>
<render-kit-class>com.sun.faces.renderkit.RenderKitImpl</render-kit-class>
But as you can see, I don't really know where to grab value for render-kit-id or if the render-kit-class is correct anyway.
Inside Mojarra package there is file jsf-ri-runtime.xml but it doesn't declare the renderers. It only declares a RenderKitFactory, under which I don't directly find anything of interest.
Pointers?
Your initial <renderer> declaration looks fine, so I tried it here.
package com.myapp;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import com.sun.faces.renderkit.html_basic.CheckboxRenderer;
public class CustomCheckboxRenderer extends CheckboxRenderer {
public CustomCheckboxRenderer() {
System.out.println("CustomCheckboxRenderer <init>");
}
#Override
public void decode(FacesContext context, UIComponent component) {
System.out.println("CustomCheckboxRenderer decode()");
super.decode(context, component);
}
#Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
System.out.println("CustomCheckboxRenderer encodeBegin()");
super.encodeBegin(context, component);
}
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
System.out.println("CustomCheckboxRenderer encodeEnd()");
super.encodeEnd(context, component);
}
}
It works fine. All get printed to stdout. Your problem lies somewhere else. I was using Mojarra 2.0.3 on Tomcat 7.0.5.
I add renderers to my faces-config.xml like so:
<faces-config>
<!--elided-->
<render-kit>
<render-kit-id>HTML_BASIC</render-kit-id>
<renderer>
<display-name>MyRenderer</display-name>
<component-family>javax.faces.Output</component-family>
<renderer-type>foo.MyRenderer</renderer-type>
<renderer-class>foo.MyRenderer</renderer-class>
<!-- TODO: attributes for tooling -->
You don't need to (and shouldn't) declare a new render kit class in this scenario.
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')}"/>