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;
}
Related
I try to add action listener into my custom component.
I have custom datatable which wrapping tomahawk datatable.
I have custom dataScroller which wrapping tomahawk datascroller.
I need to pass into dataScroller method defined in datatable.
<custom:dataScroller actionListener="#{someBean['someMethod']}"...
or
<custom:dataScroller actionListener="#{someBean.someMethod}"...
In taglib file:
<tag>
<name>dataScroller</name>
<tag-class>java.lang.Object</tag-class>
<body-content>scriptless</body-content>
...
<attribute>
<name>actionListener</name>
<deferred-method>
<method-signature>
void myMethod(javax.faces.event.ActionEvent )
</method-signature>
</deferred-method>
</attribute>
...
</tag>
in dataScroller.xhtml
<t:dataScroller id="scroller_#{for}" for="#{for}"
fastStep="#{fastStep}"
paginatorRenderLinkForActive="false"
paginator="true"
paginatorMaxPages="10"
paginatorActiveColumnStyle="font-weight:bold;"
renderFacetsIfSinglePage="#{allwaysRender}"
binding="#{applicationBean.scroll}"
actionListener="#{actionListener}"// HERE is the actionListener
immediate="false">
this gaves me javax.faces.el.EvaluationException: ... Property 'someMethod' not found on type ...
It marks t he method as property but it should be marked as method
public void someMethod(ActionEvent sae) {
LOGGER.info("Event: #0" + sae.getClass().toString());
}
Where should be the problem?
I’d like to have a (body) containing the attributes data-target, data-spy and data-twwttr-rendered and finally look like this:
<body data-target=".bs-docs-sidebar" data-spy="scroll" data-twttr-rendered="true">
Can you help me out?
This is not possible server side by default. You have to override the existing ViewRootRenderer.
1.
Create a new Java class which extends the existing ViewRootRenderer of XPages. This class has to override the method encodeHtmlBodyStart which generates the HTML code of the body attribute:
package ch.hasselba.jsf;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.component.UIViewRootEx;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import java.io.IOException;
public class ViewRootRenderer extends
com.ibm.xsp.renderkit.html_basic.ViewRootRendererEx2 {
#Override
protected void encodeHtmlBodyStart(FacesContext fc, UIViewRootEx uiRoot,
ResponseWriter rw) throws IOException {
String str = null;
// begin with body element
rw.startElement("body", uiRoot);
// add style attribute
str = uiRoot.getStyle();
if (StringUtil.isNotEmpty(str)) {
rw.writeAttribute("style", str, "style");
}
// add class attribute
str = uiRoot.getStyleClass();
if (StringUtil.isNotEmpty(str)) {
rw.writeAttribute("class", str, "styleClass");
}
// add your own attributes here
rw.writeAttribute("data-target", ".bs-docs-sidebar", "data-target");
rw.writeAttribute("data-spy", "scroll", "data-spy");
rw.writeAttribute("data-twttr-rendered", "true", "data-twttr-rendered");
// add new line
writeln(rw);
}
}
As you can see, the attributes you want to add are hardcoded above. The code before the hardcoded part (style & class attribute) is required because this is the default code.
2.
Register this ViewRootRenderer in the faces-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
<render-kit>
<renderer>
<component-family>javax.faces.ViewRoot</component-family>
<renderer-type>ch.hasselba.jsf.ViewRootRenderer</renderer-type>
<renderer-class>ch.hasselba.jsf.ViewRootRenderer</renderer-class>
</renderer>
</render-kit>
</faces-config>
3.
Use the renderedType property of your XPage to add this Renderer instead of the default one:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
rendererType="ch.hasselba.jsf.ViewRootRenderer">
</xp:view>
This is the way to add the renderer to a specific XPage only. If you want to override it in the whole application, you have to change the existing renderer-class (Step 2).
<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
<render-kit>
<renderer>
<component-family>javax.faces.ViewRoot</component-family>
<renderer-type>com.ibm.xsp.ViewRootEx</renderer-type>
<renderer-class>ch.hasselba.jsf.ViewRootRenderer</renderer-class>
</renderer>
</render-kit>
</faces-config>
You can skip Step 3 now, but be aware that this affects every XPage in your application.
You can set the body attributes on onClientLoad event:
<xp:eventHandler
event="onClientLoad"
submit="false">
<xp:this.script><![CDATA[
document.body.setAttribute('data-target', '.bs-docs-sidebar');
document.body.setAttribute('data-spy', 'scroll');
document.body.setAttribute('data-twttr-rendered', 'true');
]]></xp:this.script>
</xp:eventHandler>
The rendered page body has then the attributes you wanted:
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 want to use simplified select one tag, which would generate select items list for enums automatically. So, the result would be:
<s:enumSelectOneMenu value="#{myBean.enumValue}"/>
So, inside the component I can get the enum type and get all enum values using reflection. So, basically I need to override the only one method validateValue(..) from UiSelectOne and put the UiSelectItems list as the child there (in the same manner as it is done in Tomahawk, see SelectOneLanguage component).
But what else should be done? I need to describe tag attributes in my own taglib.xml but jsf-impl.jar does not contain facelets xml - only taglib file, so I cannot simply copy everything from there. Also, if I statically describe tag in my taglib.xml - I will have to update it manually on each new version of JSF, which is not good at all. So, which is the best way to extend the component in JSF and avoid lots of manual copy-paste work?
P.s. I'm using JSF 2.0, but composite-facelets way is not suitable for me as it produces lots of problems as composite element is wrapped by NamingContainer component. So I need only "oldschool" way to create custom components.
Thanks.
One of best (but not the easiest) way to extend some JSF components and add the behaviour while not loosing attributes is using PrimeFaces JSF plugin. Some information about it is here: http://code.google.com/p/primefaces/wiki/BuildingFromSource. Though it has some hardcoded values (name of facelets taglib and way to output directory, where generated taglib is put) and can be changed and rebuilt locally
Here is the example of PF jsf plugin
<!-- Primefaces maven plugin -->
<plugin>
<groupId>org.primefaces</groupId>
<artifactId>maven-jsf-plugin</artifactId>
<version>1.2.1-SNAPSHOT</version>
<executions>
<execution>
<id>generate-ui</id>
<phase>generate-sources</phase>
<configuration>
<uri>http://www.mycompany.com/tags</uri>
<name>rstk-tag</name>
<jsfVersion>2</jsfVersion>
<templatesDir>src/main/java-templates</templatesDir>
<componentConfigsDir>src/main/resources-maven-jsf/ui</componentConfigsDir>
<!-- <standardFacesConfig>src/main/resources-maven-jsf/standard-faces-config.xml</standardFacesConfig> -->
<!-- These are new attributes added manually to plugin source code! -->
<standardFaceletsTaglib>src/main/resources-maven-jsf/standard-facelets-taglib.xml</standardFaceletsTaglib>
<faceletsOutputDirectory>target/generated-sources/maven-jsf-plugin/META-INF</faceletsOutputDirectory>
</configuration>
<goals>
<goal>generate-components</goal>
<goal>generate-facelets-taglib</goal>
</goals>
</execution>
</executions>
</plugin>
After this the following metadata xml can be used for generation:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE component SYSTEM "../misc/component.dtd" [
<!ENTITY standard_uicomponent_attributes SYSTEM "../entities/standard_uicomponent_attributes.xml">
<!ENTITY output_component_attributes SYSTEM "../entities/output_component_attributes.xml">
<!ENTITY input_component_attributes SYSTEM "../entities/input_component_attributes.xml">
]>
<component>
<tag>enumSelectOneMenu</tag>
<tagClass>com.rstk.kasko.component.EnumSelectOneMenuTag</tagClass>
<componentClass>com.rstk.kasko.component.EnumSelectOneMenu</componentClass>
<componentType>com.rstk.kasko.component.EnumSelectOneMenu</componentType>
<componentFamily>javax.faces.SelectOne</componentFamily>
<rendererType>javax.faces.Menu</rendererType>
<parent>javax.faces.component.html.HtmlSelectOneMenu</parent>
<description>The tag for select one menu, which renders the enumerations list. No children necessary for this</description>
<attributes>
&input_component_attributes;
</attributes>
</component>
This together with EnumSelectOneMenuTemplate.java (the template, which is inserted into generated component code) allows to generate:
taglib.xml with ALL standard html select one menu attributes
The component class, which contains custom logics for rendering clidhren:
public class EnumSelectOneMenu extends HtmlSelectOneMenu {
... // Generated staff here
public boolean getRendersChildren() {
return true;
}
/**
* #see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
*/
#Override
public void encodeBegin(FacesContext context) throws IOException {
super.encodeBegin(context);
if (context.isPostback()) {
return;
}
UISelectItems selectItems = new UISelectItems();
try {
selectItems.setValue(getEnumValuesList());
} catch (Exception e) {
log.error("Failed to create enum list", e);
}
getChildren().add(selectItems);
}
/**
* Creates the list of select items of format [ENUM, enum.getDisplay()]
* #return
*/
private List getEnumValuesList() {
List result = new ArrayList();
ValueExpression ve = getValueExpression("value");
Class enumClass = ve.getType(getFacesContext().getELContext());
Method method = ReflectionUtils.findMethod(enumClass, "getDisplay", null);
for (Object e : ve.getType(getFacesContext().getELContext()).getEnumConstants()) {
result.add(new SelectItem(e, (String) ReflectionUtils.invokeMethod(method, e)));
}
return result;
}
}
Then this component can be used as simple JSF select one component (with ALL standard attributes), but does not require select items to be added each time and allows any other childre be placed there:
<s:enumSelectOneMenu value="#{polis.osMatrixType}" id="registryType">
<p:ajax listener="#{osagoPolisBean.rollOsagoEndDate}" update="osagoUsagePeriod" process="osagoTable" event="change"/>
</s:enumSelectOneMenu>
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>