Creating custom component based on Primefaces' <p:diagram> - jsf

I've been lurking this helping community for years now and yet never posted because I usually find what I need before asking.
I have read through those articles :
How to render a composite component using a custom renderer?
What is the relationship between component family, component type and renderer type? and yet i'm stuck trying to create my own component based on Primefaces <p:diagram>.
The component in itself almost fits my needs but I would need the web browser to be able to correctly interpret HTML tags such as <mark>, <strong> for the data attribute of the Element of the <p:diagram>. I have yet to found a solution without implementing my own component.Knowing a bit how JSF's <h:outputText> gives the option (through the escape tag) to interpret HTML tags correctly, I thought about adding this tag and it's behaviour to the <p:diagram> component and make my own component (I may also have to add further customing later on).Here is my taglib :
<?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://myNamespace</namespace>
<tag>
<tag-name>logigramme</tag-name>
<description><![CDATA[]]></description>
<component>
<component-type>myComponent.component.type</component-type>
<renderer-type>myComponentRenderer.renderer.type</renderer-type>
</component>
<attribute>
<description><![CDATA[Unique identifier of the component in a namingContainer.]]></description>
<name>id</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Boolean value to specify the rendering of the component, when set to false component will not be rendered.]]></description>
<name>rendered</name>
<required>false</required>
<type>java.lang.Boolean</type>
</attribute>
<attribute>
<description><![CDATA[An el expression referring to a server side UIComponent instance in a backing bean.]]></description>
<name>binding</name>
<required>false</required>
<type>javax.faces.component.UIComponent</type>
</attribute>
<attribute>
<description><![CDATA[Value of the component.]]></description>
<name>value</name>
<required>false</required>
<type>java.lang.Object</type>
</attribute>
<attribute>
<description><![CDATA[An el expression or a literal text that defines a converter for the component. When it's an EL expression, it's resolved to a converter instance.
In case it's a static text, it must refer to a converter id.]]></description>
<name>converter</name>
<required>false</required>
<type>java.faces.convert.Converter</type>
</attribute>
<attribute>
<description><![CDATA[Name of the client side widget.]]></description>
<name>widgetVar</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Name of the iterator variable used to refer each data.]]></description>
<name>var</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Inline style of the component.]]></description>
<name>style</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Style class of the component.]]></description>
<name>styleClass</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description><![CDATA[Flag indicating that characters that are sensitive in HTML and XML markup must be escaped. This flag is set to "true" by default.]]></description>
<name>escape</name>
<required>false</required>
<type>java.lang.Boolean</type>
</attribute>
</tag>
Here is my component :
package myPackage;
import javax.faces.component.FacesComponent;
import org.primefaces.component.diagram.Diagram;
#FacesComponent("logigramme")
public class Logigramme extends Diagram {
public static final String COMPONENT_TYPE = "myComponent.component.type";
public static final String DEFAULT_RENDERER = "myComponentRenderer.renderer.type";
protected enum PropertyKeys {
widgetVar, var, style, styleClass, escape;
String toString;
PropertyKeys(String toString) {
this.toString = toString;
}
PropertyKeys() {
}
public String toString() {
return ((this.toString != null) ? this.toString : super.toString());
}
}
public Logigramme() {
setRendererType(DEFAULT_RENDERER);
}
public String getEscape() {
return (String) getStateHelper().eval(PropertyKeys.escape, null);
}
}
Here is my custom renderer :
package myPackage;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.FacesRenderer;
import org.primefaces.component.diagram.Diagram;
import org.primefaces.component.diagram.DiagramRenderer;
import org.primefaces.model.diagram.Connection;
import org.primefaces.model.diagram.DiagramModel;
import org.primefaces.model.diagram.Element;
import org.primefaces.model.diagram.connector.Connector;
import org.primefaces.model.diagram.endpoint.EndPoint;
import org.primefaces.model.diagram.overlay.Overlay;
import org.primefaces.renderkit.CoreRenderer;
import org.primefaces.util.SharedStringBuilder;
import org.primefaces.util.WidgetBuilder;
#FacesRenderer(componentFamily = Diagram.COMPONENT_FAMILY, rendererType=Logigramme.DEFAULT_RENDERER)
public class LogigrammeRenderer extends DiagramRenderer {
#Override
public void decode(FacesContext context, UIComponent component) {
Logigramme logigramme = (Logigramme) component;
if (logigramme.isConnectRequest(context)) {
decodeNewConnection(context, logigramme);
} else if (logigramme.isDisconnectRequest(context)) {
decodeDisconnection(context, logigramme);
} else if (logigramme.isConnectionChangeRequest(context)) {
decodeConnectionChange(context, logigramme);
}
decodeBehaviors(context, component);
}
[...]
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
Logigramme logigramme = (Logigramme) component;
encodeMarkup(context, logigramme);
encodeScript(context, logigramme);
}
[...]
protected void encodeMarkup(FacesContext context, Logigramme logigramme) throws IOException {
ResponseWriter writer = context.getResponseWriter();
DiagramModel model = (DiagramModel) logigramme.getValue();
String clientId = logigramme.getClientId(context);
String style = logigramme.getStyle();
String styleClass = logigramme.getStyleClass();
styleClass = (styleClass == null) ? Logigramme.CONTAINER_CLASS : Logigramme.CONTAINER_CLASS + " " + styleClass;
UIComponent elementFacet = logigramme.getFacet("element");
Map<String, Object> requestMap = context.getExternalContext().getRequestMap();
String var = logigramme.getVar();
Boolean escape = Boolean.valueOf(logigramme.getEscape());
writer.startElement("div", logigramme);
writer.writeAttribute("id", logigramme.getClientId(context), null);
writer.writeAttribute("class", styleClass, null);
if (style != null) {
writer.writeAttribute("style", style, null);
}
if (model != null) {
List<Element> elements = model.getElements();
if (elements != null && !elements.isEmpty()) {
for (int i = 0; i < elements.size(); i++) {
Element element = elements.get(i);
String elementClass = element.getStyleClass();
elementClass = (elementClass == null) ? Logigramme.ELEMENT_CLASS : Logigramme.ELEMENT_CLASS + " " + elementClass;
if (element.isDraggable()) {
elementClass = elementClass + " " + Logigramme.DRAGGABLE_ELEMENT_CLASS;
}
Object data = element.getData();
String x = element.getX();
String y = element.getY();
String coords = "left:" + x + ";top:" + y;
writer.startElement("div", null);
writer.writeAttribute("id", clientId + "-" + element.getId(), null);
writer.writeAttribute("class", elementClass, null);
writer.writeAttribute("style", coords, null);
if (elementFacet != null && var != null) {
requestMap.put(var, data);
elementFacet.encodeAll(context);
} else if (data != null) {
if (escape == null || escape) {
writer.writeText(data, null);
} else {
writer.write(data.toString());
}
}
writer.endElement("div");
}
}
if (var != null) {
requestMap.remove(var);
}
}
writer.endElement("div");
}
}
Considering I used annotations on the Renderer, i did not modify the faces-config.xml.
I then call my new component in the view with :
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.org/ui" xmlns:odc="http://myNamespace">
[...]
<odc:logigramme value="#{myBean.model}" styleClass="ui-widget-content" widgetVar="logigrammeWV" escape="false"/>
with myBean being my Backing bean containing my DefaultDiagramModel (primefaces object).
When i display my page, I have the following error (i took the liberty to crop it, I can give the full stacktrace if needed) :
INFOS: Facelet[/views/incident/listeIncidents.xhtml] was modified # 11:06:19, flushing component applied # 11:06:15
juil. 26, 2018 11:06:19 AM com.sun.faces.application.ApplicationImpl createComponentApplyAnnotations
GRAVE: JSF1068 : Impossible d’instancier un composant dont le type est myComponent.component.type
javax.faces.FacesException: Erreur d’expression : objet nommé «myComponent.component.type» non détecté
at com.sun.faces.application.ApplicationImpl.createComponentApplyAnnotations(ApplicationImpl.java:1933)
at com.sun.faces.application.ApplicationImpl.createComponent(ApplicationImpl.java:1168)
at javax.faces.application.ApplicationWrapper.createComponent(ApplicationWrapper.java:637)
[...]
Loosely translated, the error means that JSF is unable to instantiate a Component of type myComponent.component.type (which would be Diagram.COMPONENT_FAMILY aka org.primefaces.component)
So finally, my questions : any idea of what i'm doing wrong ? Did I forget anything ? Does anyone ever had to create a custom component based of Primefaces' diagram ?
Thanks for your help guys :)

Usually I would comment and not answer but I see a couple of problems with your code.
In my code where I override the Datatable I had to add this to my faces-config.xml for it to pick it up. I would have thought your #FacesComponent annotations would have done that.
<!-- Extend PF Datatable component and rendering to fix filter map handling -->
<component>
<component-type>org.primefaces.component.DataTable</component-type>
<component-class>com.stuff.web.faces.MyDataTable</component-class>
</component>
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.DataTableRenderer</renderer-type>
<renderer-class>com.stuff.faces.MyDataTableRenderer</renderer-class>
</renderer>
</render-kit>
Second, unless your example is truncated I don't see a "setter" for your Escape property only a getter. You will need both for it to set the value.

Related

Does inputtextarea trigger any event after a suggestion is clicked?

I have an inputtextarea which calls a completeMethod after a query is entered... that is working fine and the suggestions are displaying, but I woul'd need to catch the ajax event in order to know which suggestion the user has picked. Is it possible?
You are trying to use the ajax itemSelect event like so:
<h:form>
<p:inputTextarea widgetVar="textarea" completeMethod="#{myBean.complete}">
<p:ajax event="itemSelect" listener="#{myBean.onSelect}" />
</p:inputTextarea>
</h:form>
Managed bean methods:
public List<String> complete(String filter) {
List<String> result = new ArrayList<String>();
for (int i = 0; i < 10; i++)
result.add(filter + i);
return result;
}
public void onSelect(SelectEvent<String> e) {
System.out.println(e.getObject());
}
This does not work in Primefaces 7.0 (and probably earlier) because the renderer of the inputTextarea fails to add the clientBehavior configuration which is fixed as of Version 7.1.
In the browser javascript console input:
PF('textarea').cfg.behaviors
> undefined
The result undefined indicates missing client behavior configuration.
You can work around this by overriding the InputTextareaRenderer.encodeScript() method:
package my.package;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import org.primefaces.component.inputtextarea.InputTextarea;
import org.primefaces.component.inputtextarea.InputTextareaRenderer;
import org.primefaces.expression.SearchExpressionFacade;
import org.primefaces.util.WidgetBuilder;
public class CustomInputTextareaRenderer extends InputTextareaRenderer {
#Override
protected void encodeScript(FacesContext context, InputTextarea inputTextarea) throws IOException {
String clientId = inputTextarea.getClientId(context);
boolean autoResize = inputTextarea.isAutoResize();
String counter = inputTextarea.getCounter();
WidgetBuilder wb = getWidgetBuilder(context);
wb.init("InputTextarea", inputTextarea.resolveWidgetVar(), clientId).attr("autoResize", autoResize)
.attr("maxlength", inputTextarea.getMaxlength(), Integer.MAX_VALUE);
if (counter != null) {
UIComponent counterComponent = SearchExpressionFacade.resolveComponent(context, inputTextarea, counter);
wb.attr("counter", counterComponent.getClientId(context)).attr("counterTemplate",
inputTextarea.getCounterTemplate(), null);
}
if (inputTextarea.getCompleteMethod() != null) {
wb.attr("autoComplete", true).attr("minQueryLength", inputTextarea.getMinQueryLength())
.attr("queryDelay", inputTextarea.getQueryDelay())
.attr("scrollHeight", inputTextarea.getScrollHeight(), Integer.MAX_VALUE);
}
// additional line enabling AJAX 'itemSelect' event handling.
encodeClientBehaviors(context, inputTextarea);
wb.finish();
}
}
In faces config, add the renderer:
<faces-config>
...
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.InputTextareaRenderer</renderer-type>
<renderer-class>my.package.CustomInputTextareaRenderer</renderer-class>
</renderer>
</render-kit>
</faces-config>
Quick indication that it works in javascript console:
PF('textarea').cfg.behaviors
> Object { itemSelect: itemSelect() }

How to override h:selectOneRadio renderer? Where is the renderer class in jsf-impl?

Is it possible to override renderer used by <h:selectOneRadio>? I tried to find the class from jsf-impl package of JSF 2.2 but didn't find it. The reason I want to do this is to get rid of the table it generates.
Is it possible to override renderer used by h:selectOneRadio?
Yes, surely it is. Otherwise, UI component libraries like PrimeFaces couldn't exist.
I tried to find the class from jsf-impl package but didn't find it.
The exact class depends on the JSF implementation you're using. If it's Mojarra, then it's the com.sun.faces.renderkit.html_basic.RadioRenderer class. If it's MyFaces, then it's the org.apache.myfaces.renderkit.html.HtmlRadioRenderer class.
In order to properly override it, just extend the class and override methods where necessary and register it as follows in your faces-config.xml:
<render-kit>
<renderer>
<component-family>javax.faces.SelectOne</component-family>
<renderer-type>javax.faces.Radio</renderer-type>
<renderer-class>com.example.MyRadioRenderer</renderer-class>
</renderer>
</render-kit>
Keep in mind that you're this way tight-coupling the renderer to the specific JSF impl/version. Such an extended renderer is not compatible with a different JSF implementation (i.e. your app wouldn't deploy when you ever replace Mojarra by MyFaces) and may possibly break when the current JSF implementation has been updated to a newer version. If you worry about this, consider writing the renderer entirely from scratch, like PrimeFaces et.al. do.
The reason I want to do this is to get rid of the table it generates.
Consider looking at Tomahawk or PrimeFaces instead of reinventing the wheel. They have respectively a <t:selectOneRadio layout="spread"><t:radio> and <p:selectOneRadio layout="custom"><p:radioButton> which allows you positioning those things everywhere you want.
See also:
<h:selectOneRadio> renders table element, how to avoid this?
I added
<render-kit>
<renderer>
<component-family>javax.faces.SelectOne</component-family>
<renderer-type>javax.faces.Radio</renderer-type>
<renderer-class>com.sial.ecommerce.configurator.ui.model.RadioRendererWithoutDataTable</renderer-class>
</renderer>
</render-kit>
to faces-config.xml.
And created a class which extends com.sun.faces.renderkit.html_basic.RadioRenderer And I did override the method encodeEnd then commented out the code which adding table elements.
public class RadioRendererWithoutDataTable extends com.sun.faces.renderkit.html_basic.RadioRenderer {
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
rendererParamsNotNull(context, component);
if (!shouldEncode(component)) {
return;
}
ResponseWriter writer = context.getResponseWriter();
assert (writer != null);
String alignStr;
Object borderObj;
boolean alignVertical = false;
int border = 0;
if (null != (alignStr = (String) component.getAttributes().get("layout"))) {
alignVertical = alignStr.equalsIgnoreCase("pageDirection");
}
if (null != (borderObj = component.getAttributes().get("border"))) {
border = (Integer) borderObj;
}
Converter converter = null;
if (component instanceof ValueHolder) {
converter = ((ValueHolder) component).getConverter();
}
// renderBeginText(component, border, alignVertical, context, true);
Iterator<SelectItem> items = RenderKitUtils.getSelectItems(context, component);
Object currentSelections = getCurrentSelectedValues(component);
Object[] submittedValues = getSubmittedSelectedValues(component);
Map<String, Object> attributes = component.getAttributes();
OptionComponentInfo optionInfo = new OptionComponentInfo((String) attributes.get("disabledClass"),
(String) attributes.get("enabledClass"), (String) attributes.get("unselectedClass"),
(String) attributes.get("selectedClass"), Util.componentIsDisabled(component), isHideNoSelection(component));
int idx = -1;
while (items.hasNext()) {
SelectItem curItem = items.next();
idx++;
// If we come across a group of options, render them as a nested
// table.
if (curItem instanceof SelectItemGroup) {
// write out the label for the group.
if (curItem.getLabel() != null) {
// if (alignVertical) {
// writer.startElement("tr", component);
// }
//writer.startElement("td", component);
writer.writeText(curItem.getLabel(), component, "label");
// writer.endElement("td");
// if (alignVertical) {
// writer.endElement("tr");
// }
}
// if (alignVertical) {
// writer.startElement("tr", component);
// }
// writer.startElement("td", component);
// writer.writeText("\n", component, null);
// renderBeginText(component, 0, alignVertical, context, false);
// render options of this group.
SelectItem[] itemsArray = ((SelectItemGroup) curItem).getSelectItems();
for (int i = 0; i < itemsArray.length; ++i) {
renderOption(context, component, converter, itemsArray[i], currentSelections, submittedValues, alignVertical, i,
optionInfo);
}
// renderEndText(component, alignVertical, context);
// writer.endElement("td");
// if (alignVertical) {
// writer.endElement("tr");
// writer.writeText("\n", component, null);
// }
} else {
renderOption(context, component, converter, curItem, currentSelections, submittedValues, alignVertical, idx, optionInfo);
}
}
//renderEndText(component, alignVertical, context);
}
Then it worked for me.
When I given
<h:selectOneRadio >
<f:selectItem itemValue="1" itemLabel="Item 1" />
<f:selectItem itemValue="2" itemLabel="Item 2" />
</h:selectOneRadio>
in my jsf page.
It converted to
<input type="radio" name="bulkForm:j_idt224" id="bulkForm:j_idt224:0" value="1"><label for="bulkForm:j_idt224:0"> Item 1</label>
<input type="radio" name="bulkForm:j_idt224" id="bulkForm:j_idt224:1" value="2"><label for="bulkForm:j_idt224:1"> Item 2</label>
which was what I need.

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>

JSF - custom component, problem with expression for attribute

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>

JSPTag: Exception - This attribute does not support request time values

I am getting the "This attribute does not support request time values." while compiling my custom jsp tag implementation.
My TLD file
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>My Extension</shortname>
<info>Customization Extensions</info>
<tag>
<name>requireDisplay</name>
<tagclass>com.view.LinksTag</tagclass>
<teiclass>com.view.LinksTagExtraInfo</teiclass>
<bodycontent>JSP</bodycontent>
<info> My Tags Exercise
</info>
<attribute>
<name>viewID</name>
<required>true</required>
<rtexpvalue>true</rtexpvalue>
</attribute>
<attribute>
<name>viewType</name>
<required>true</required>
<rtexpvalue>true</rtexpvalue>
</attribute>
</tag>
</taglib>
My JSP Tag Implementation has the following method
/**
* doStartTag is called by the JSP container when the tag is encountered
*/
public int doStartTag() throws JspException {
try {
JspWriter out = pageContext.getOut();
out.println(getContent());
} catch (Exception ex) {
throw new ElmsUncheckedException(ex);
}
// Must return SKIP_PAGE because we are done with the content.
return SKIP_BODY;
}
/**
* getContent is called by the startTag to print the Request View button
* contents.
*/
private String getContent() {
String linkURL = ViewConstants.BASE_URL;
StringBuffer sbuffer = new StringBuffer();
sbuffer.append("<form name=\"postView\" action=\"" + linkURL + "\" target=\"_blank\" method=\"POST\">\n");
sbuffer.append("<input type=\"hidden\" name=\"ID\" value=\"" + viewID + "\" />\n");
sbuffer.append("<input type=\"hidden\" name=\"Type\" value=\"" + viewType + "\" />\n");
sbuffer.append("<tr>\n");
sbuffer.append("<td class=\"BodyBG\">\n");
sbuffer
.append("<button type=\"submit\" class=\"GridButton\" name=\"dispatch\" value=\"postView\">postView</button> \n");
sbuffer.append("</td><td width=\"1\"></td>\n");
sbuffer.append("</tr>\n");
sbuffer.append("</form>\n");
return sbuffer.toString();
}
public int doEndTag() throws JspException {
resetVariables();
return EVAL_PAGE;
}
my jsp has the following
<%# taglib uri="/WEB-INF/view.tld" prefix="cmd" %>
.....
<cmd:requireDisplay viewID="<%=vox.getViewID()%>" viewType="<%=vox.getViewType()%>"/>
But i am getting the following exception
[jspc] Error encountered while compiling 'jspURI'
view_details.jsp:136:22: The required attribute "viewID" is missing.
<cmd:requireDisplay viewID="<%=vox.getViewID()%>" viewType="<%=vox.getViewType()%>"/>
^----------------------^
view_details.jsp:136:22: The required attribute "viewType" is missing.
<cmd:requireDisplay viewID="<%=vox.getViewID()%>" viewType="<%=vox.getViewType()%>"/>
^----------------------^
view_details.jsp:136:42: This attribute does not support request time values.
<cmd:requireDisplay viewID="<%=vox.getViewID()%>" viewType="<%=vox.getViewType()%>"/>
^---------^
view_details.jsp:136:57: This attribute does not support request time values.
<cmd:requireDisplay viewID="<%=vox.getViewID()%>" viewType="<%=vox.getViewType()%>"/>
^----------^
Am i missing anything? As in the tld, even i tried to give TagExtraInfo too. But no luck.
Any help is much appreciated.
I figured out the issue.
It is a typo :(
<rtexpvalue>true</rtexpvalue>
entry suppose to be
<rtexprvalue>true</rtexprvalue>

Resources