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:
Related
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 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;
}
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 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>
I want to create custom component with attribute "title" that can have expression but I get this error:
Unable to convert string "#{myBean.text}" to class "javax.el.ValueExpression" for attribute "title": Property Editor not registered with the PropertyEditorManager
Caused by:
org.apache.jasper.JasperException - Unable to convert string "#{myBean.text}" to class "javax.el.ValueExpression" for attribute "title": Property Editor not registered with the PropertyEditorManager
My classes:
<d:ticker title="#{myBean.text}">
<f:verbatim>Hello JSF Custom Component</f:verbatim>
</d:ticker>
MyBean.java
public class MyBean {
private String text = "TITLE!!!!";
public String getText() {
return text;
}
}
TickerTag.java
private ValueExpression title = null;
public void setTitle(ValueExpression title)
{
this.title = title;
}
protected void setProperties(UIComponent component) {
super.setProperties(component);
if (title != null) {
if (!title.isLiteralText()) {
component.setValueExpression("title", title);
} else {
component.getAttributes().put("title",title.getExpressionString());
}
}
taglib.tld
<taglib version="2.1" 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 web-jsptaglibrary_2_1.xsd">
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>d</short-name>
<uri>http://jsftutorials.com/</uri>
<tag>
<name>ticker</name>
<tag-class>ticker.TickerTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>title</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
anybody see the problem?
I encountered the same problem, and was able to solve it by including the deferred-value tag in my taglib.tld file. It's required when the component has an attribute that can be set with an EL expression. The 'type' tag is the type that the EL expression should evaluate to.
taglib.tld:
<tag>
<name>CustomComponent</name>
<tag-class>com.test.components.CustomComponent</tag-class>
<attribute>
<name>someAttribute</name>
<description>The custom attribute</description>
<deferred-value>
<type>java.lang.String</type>
</deferred-value>
</attribute>
</tag>