JSF MethodExpression isn't triggering my backing bean action - jsf

I have the problem that my code isn't triggering the action in my backing bean. The code is as follows:
HtmlCommandButton replyCommentButton = new HtmlCommandButton();
replyCommentButton.setId("replyCommentButton" + commentCounter);
replyCommentButton.setValue("Create reply");
String action = "#{Handler.action_replyToComment}";
MethodExpression methodExpression =
FacesContext.getCurrentInstance().getApplication().getExpressionFactory().
createMethodExpression(FacesContext.getCurrentInstance().getELContext(), action, null,
new Class<?>[0]);
replyCommentButton.setActionExpression(methodExpression);
In my backing bean called RequestHandlerBean, defined in FacesConfig as Handler, I have the following code:
public void action_replyToComment() {
logger.info("Homemade action called!!!");
System.out.println("Homemade action called!!!");
}
Does anyone spot why nothing happens when I click the button? It isn't triggering the event properly. The source of the html code is as follows:
<input id="replyCommentButton1" type="submit" value="Create reply"
name="replyCommentButton1"/>
As we can see there's no action defined in the HTML.
Edit 2: I just found out in Javadoc that my action method has to be a public String. I've changed this in my backing bean now so the code in my backing bean is:
public String action_replyToComment() {
logger.info("Homemade action called!!!");
System.out.println("Homemade action called!!!");
return null;
}
Edit2: I've also made sure that I have it encapsulated within a tag, but still no luck. Shouldn't there be a action attribute on the element?
Edit3: My bean is defined in my faces-config like this:
<managed-bean>
<description>
Handles the specific request.
</description>
<managed-bean-name>Handler</managed-bean-name>
<managed-bean-class>no.ngt.tech.rt2.beans.RequestHandlerBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
Also if I choose to output in my JSF page like this:
<a4j:commandButton action="#{Handler.action_replyToComment}" value="Reply" />
That works perfectly
EDIT 4 - MY JSP PAGE
Note Ive also tried using the depreciated setAction(methodBinding) now too, but sadly it didnt work either.
<%# include file="_includes.jsp" %>
<f:view>
<html>
<head>
<title><h:outputText value="#{msgs.title}" /></title>
</head>
<body>
<br /><br /><br />
<%# include file="_menu.jsp" %>
<rich:tabPanel switchType="client">
<rich:tab id="commentsTab" label="Comments" rendered="#{Handler.editRequest}">
<h:form>
<ngt:commentTree binding="#{Handler.commentTree}" value="#{Handler.comments}" />
<br />
<a4j:commandButton action="#{Handler.action_replyToComment}" value="testbutton" />
</h:form>
</rich:tab>
</rich:tabPanel>
</body>
</html>
</f:view>
menu.jsp:
<h:form>
<rich:toolBar itemSeparator="line" styleClass="toolbar" contentClass="toolbar" height="22">
<rich:toolBarGroup>
<rich:menuItem submitMode="server" value="Front" action="#{newRT.action_showFront}" />
</rich:toolBarGroup>
<rich:toolBarGroup>
<rich:menuItem submitMode="server" value="New request" action="#{Step.action_showSteps}" />
</rich:toolBarGroup>
<rich:toolBarGroup>
<rich:menuItem submitMode="server" value="Requests" action="#{Handler.action_showRequestsView}" />
</rich:toolBarGroup>
<rich:toolBarGroup>
<rich:menuItem submitMode="server" value="Control-panel" action="#" />
</rich:toolBarGroup>
<rich:toolBarGroup location="right">
<h:inputText styleClass="barsearch" value="#{Handler.search}" />
<a4j:commandButton styleClass="barsearchbutton" action="#{Handler.action_GetRequestFromID}" value="Search" />
</rich:toolBarGroup>
</rich:toolBar>
</h:form>
<br/><br/>
Includes.jsp
<%# taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<%# taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="a4j" uri="http://richfaces.org/a4j" %>
<%# taglib prefix="rich" uri="http://richfaces.org/rich"%>
<%# taglib prefix="ngt" uri="http://MySpecialTagLib.no/"%>
Edit 7 - Java code for UIComponent:
This is CommentsTreeUI.java:
public class CommentsTreeUI extends UIOutput {
private static Logger logger = Logger.getLogger(CommentsTreeUI.class.getName());
#Override
public void encodeBegin(FacesContext context) throws IOException {
List<CommentTag> value = (List<CommentTag>) getAttributes().get("value");
int commentCounter = 0;
if (value != null) {
for (CommentTag comment : value) {
commentCounter++;
ResponseWriter commentContainerWriter = context.getResponseWriter();
commentContainerWriter.startElement("div", this);
commentContainerWriter.writeAttribute("id", "Comment" + commentCounter, null);
String width = comment.getWidth();
String height = comment.getHeight();
String style = comment.getStyle();
style = (style != null) ? style + ";" : "";
if (width != null) {
style += "width:" + width + ";";
}
if (height != null) {
style += "height:" + height + ";";
}
commentContainerWriter.writeAttribute("style", style, null);
String newComment = comment.getNewComment();
if (newComment == null) {
newComment = "false";
}
String level = comment.getLevel();
if (level != null) {
level = "commentContainerLevel" + level + newComment;
}
commentContainerWriter.writeAttribute("class", level, null);
String title = comment.getTitle();
if (title != null) {
commentContainerWriter.writeAttribute("title", title, null);
}
String titleText = comment.getTitleText();
if (titleText != null) {
ResponseWriter titleTextWriter = context.getResponseWriter();
UIOutput titleTextComponent = new UIOutput();
titleTextWriter.startElement("div", titleTextComponent);
titleTextWriter.writeAttribute("class", "commentHeaderText" + newComment, null);
titleTextWriter.writeText(titleText + " | ", null);
titleTextWriter.startElement("a", titleTextComponent);
titleTextWriter.writeAttribute("onclick", "showCommentReply('CommentReply" + commentCounter + "')", null);
titleTextWriter.writeAttribute("class", "reply", null);
titleTextWriter.writeText("Reply", null);
titleTextWriter.endElement("a");
titleTextWriter.endElement("div");
}
String commentBody = comment.getCommentBody();
if (commentBody != null) {
ResponseWriter commentBodyWriter = context.getResponseWriter();
UIOutput commentBodyComponent = new UIOutput();
commentBodyWriter.startElement("div", commentBodyComponent);
commentBodyWriter.writeText(commentBody, null);
commentBodyWriter.endElement("div");
}
ResponseWriter replyContainerWriter = context.getResponseWriter();
UIOutput replyContainerComponent = new UIOutput();
replyContainerWriter.startElement("div", replyContainerComponent);
commentContainerWriter.writeAttribute("id", "CommentReply" + commentCounter, null);
replyContainerWriter.writeAttribute("class", "replyContainer", null);
ResponseWriter replyHeaderWriter = context.getResponseWriter();
UIOutput replyHeaderComponent = new UIOutput();
replyHeaderWriter.startElement("div", replyHeaderComponent);
replyHeaderWriter.writeAttribute("class", "replyHeaderContainer", null);
replyHeaderWriter.endElement("div");
ResponseWriter replyFormWriter = context.getResponseWriter();
UIInput replyFormComponent = new UIInput();
replyFormWriter.startElement("fieldset", replyFormComponent);
replyFormWriter.startElement("textarea", replyFormComponent);
replyFormWriter.writeAttribute("type", "textarea", null);
replyFormWriter.writeAttribute("rows", "5", null);
replyFormWriter.writeAttribute("cols", "76", null);
replyFormWriter.writeText("Write your answer here", null);
replyFormWriter.endElement("textarea");
//TODO: Fix so button has action to backing bean
HtmlAjaxCommandButton replyCommentButton = new HtmlAjaxCommandButton();
replyCommentButton.setId("replyCommentButton" + commentCounter);
replyCommentButton.setValue("Create reply");
String action = "#{RequestHandlerBean.action_replyToComment}";
//replyCommentButton.setReRender("commentsTree");
ExpressionFactory factory = context.getApplication().getExpressionFactory();
Class [] argtypes=new Class[1];
argtypes[0]=ActionEvent.class;
MethodExpression replyActionExpression = factory.createMethodExpression(context.getELContext(), action, null, argtypes);
replyCommentButton.setActionExpression(replyActionExpression);
MethodExpression methodExpression = context.getCurrentInstance().getApplication().getExpressionFactory().
createMethodExpression(context.getCurrentInstance().getELContext(), action, null, new Class<?>[0]);
replyCommentButton.setActionExpression(methodExpression);
/*
replyCommentButton.setAction(context.getApplication().createMethodBinding(action, argtypes));
*/
replyCommentButton.encodeAll(context);
//Todo above
replyFormWriter.writeText(" ", null);
replyFormWriter.startElement("input", replyFormComponent);
replyFormWriter.writeAttribute("type", "button", null);
replyFormWriter.writeAttribute("value", "Cancel ", null);
replyFormWriter.writeAttribute("onclick", "hideCommentReply('CommentReply" + commentCounter + "')", title);
replyFormWriter.endElement("input");
replyFormWriter.endElement("fieldset");
replyContainerWriter.endElement("div");
commentContainerWriter.endElement("div");
}
} else { //value==null
ResponseWriter writer = context.getResponseWriter();
writer.startElement("div", this);
writer.writeAttribute("id", getClientId(context), null);
String width = (String) getAttributes().get("width");
String height = (String) getAttributes().get("height");
String style = (String) getAttributes().get("style");
style = (style != null) ? style + ";" : "";
if (width != null) {
style += "width:" + width + ";";
}
if (height != null) {
style += "height:" + height + ";";
}
writer.writeAttribute("style", style, null);
String styleClass = (String) getAttributes().get("styleClass");
if (styleClass != null) {
writer.writeAttribute("class", styleClass, null);
}
String title = (String) getAttributes().get("title");
if (title != null) {
writer.writeAttribute("title", title, null);
}
}
}
#Override
public void encodeEnd(FacesContext context) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.endElement("div");
}
This is CommenstTreeTag:
public class CommentsTreeTag extends UIComponentTag {
String style;
String styleClass;
String title;
String width;
String height;
String value;
Long parentId;
public void release() {
// the super class method should be called
super.release();
style = null;
styleClass = null;
title = null;
height = null;
width = null;
parentId = null;
value = null;
}
#Override
protected void setProperties(UIComponent component) {
// the super class method should be called
super.setProperties(component);
if (style != null) {
component.getAttributes().put("style", style);
}
if (styleClass != null) {
component.getAttributes().put("styleClass", styleClass);
}
if (width != null) {
component.getAttributes().put("width", width);
}
if (height != null) {
component.getAttributes().put("height", height);
}
if (title != null) {
if (isValueReference(title)) {
ValueBinding vb =
getFacesContext().getApplication().createValueBinding(title);
component.setValueBinding("title", vb);
} else {
component.getAttributes().put("title", title);
}
}
if (value != null) {
if (isValueReference(value)) {
ValueBinding vb =
getFacesContext().getApplication().createValueBinding(value);
component.setValueBinding("value", vb);
getFacesContext().getApplication().createValueBinding(value);
} else {
component.getAttributes().put("value", value);
}
}
if (parentId != null) {
component.getAttributes().put("parentId", parentId);
}
}
public String getComponentType() {
return "commentTree";
}
public String getRendererType() {
// null means the component renders itself
return null;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
public String getWidth() {
return width;
}
public void setWidth(String width) {
this.width = width;
}
public String getStyle() {
return style;
}
public void setStyle(String style) {
this.style = style;
}
public String getStyleClass() {
return styleClass;
}
public void setStyleClass(String styleClass) {
this.styleClass = styleClass;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

I think the problem you encounter is due to the way you add the commandButton in the JSF component tree, or, to be more exact, the way uou do NOT add it in the component tree.
As you do not attach the commandButton to the JSF component tree, when this component is rendered (by your call to .encodeAll() method), it does not find the form where this component is nested.
Thus, the HTML input is not created correctly.
Maybe you can try to add the commandButton created to the form directly, before asking it to be rendered. You can do that with the following code:
// Search for the component HtmlForm that is a (in)direct parent of the current component
private UIComponent getCurrentForm(UIComponent currentComponent) {
UIComponent parent = currentComponent.getParent();
while ((parent != null) && !(parent instanceof HtmlForm)) {
parent = parent.getParent();
}
return parent;
}
public void encodeBegin(FacesContext context) throws IOException {
...
HtmlAjaxCommandButton replyCommentButton = new HtmlAjaxCommandButton();
...
// Add the command button in the form that contains your custom component...
UIComponent form = getCurrentForm(this);
if (form != null) {
form.getChildren().add(replyCommentButton);
}
replyCommentButton.encodeAll(context);
...
}
note that I've tested this code, so maybe you have to modify it a little...
If it still doesn't work, maybe you will need to review you code in order to create the commandButton outside the render class or outside the encodeBegin... I don't think it is a good idea to modify the JSF components tree during the rendering phase...

Your code seems to be correct (indeed, the method must return a String, but it can also return void, even if it is not recommanded to write an action method like that).
Are you sure that the commandButton created is nested within a component? Otherwise, it will not work.
By that, I mean that the (created in Java or JSF page, no matter), must have a direct or indirect parent (in the JSF component tree) a component. For example:
<h:form>
...
<h:commandButton .../>
...
</h:form>
Have you any Javascript error when you click on the problem?
Edit regarding the comment.
Edit 2, regarding my comment on the main post:
Maybe you can try to attach the action on your button with the old way (ok, it is deprecated, but just give it a try):
replyCommandButton.setAction(context.getApplication().createMethodBinding("#{Handler.action_replyToComment}", null));

Related

Get composite component inputs' converted values

I have a composite component made of two p:calendars:
<cc:interface componentType="testComponent">
<cc:attribute name="value" type="org.primefaces.test.DateRange" />
</cc:interface>
<cc:implementation>
<c:set value="dd-MM-yyyy" var="pattern" />
<p:panelGrid columns="2">
<p:outputLabel for="from" value="From" />
<p:calendar id="from" binding="#{cc.from}" pattern="#{pattern}" />
<p:outputLabel for="to" value="To" />
<p:calendar id="to" binding="#{cc.to}" pattern="#{pattern}" />
</p:panelGrid>
</cc:implementation>
Class DateRange:
public class DateRange {
private Date from;
private Date to;
public DateRange() {
}
public DateRange(Date from, Date to) {
this.from = from;
this.to = to;
}
public Date getFrom() {
return from;
}
public void setFrom(Date from) {
this.from = from;
}
public Date getTo() {
return to;
}
public void setTo(Date to) {
this.to = to;
}
#Override
public String toString() {
return from + " --> " + to;
}
}
Backing class:
#FacesComponent("testComponent")
public class TestComponent extends UIInput implements NamingContainer {
private UIInput from;
private UIInput to;
private static final String DELIMITER = "-->";
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
DateRange range = (DateRange) getValue();
System.out.println(range);
if (range != null) {
from.setValue(range.getFrom());
to.setValue(range.getTo());
}
super.encodeBegin(context);
}
#Override
public Object getSubmittedValue() {
if (from == null || to == null) return null;
String submittedFrom = (String) from.getSubmittedValue();
String submittedTo = (String) to.getSubmittedValue();
if (submittedFrom == null) {
submittedFrom = "";
}
if (submittedTo == null) {
submittedTo = "";
}
return submittedFrom + DELIMITER + submittedTo;
}
#Override
protected Object getConvertedValue(FacesContext context, Object newSubmittedValue) throws ConverterException {
String submitted = (String) newSubmittedValue;
if (submitted == null || submitted.isEmpty()) {
return null;
}
String[] split = submitted.split(DELIMITER);
String fromStr = split.length > 0 ? split[0] : "";
String toStr = split.length > 1 ? split[1] : "";
// TODO: Do I need to perform a manual conversion of the inputs???
return new DateRange((Date) fromObj, (Date) toObj);
}
getConvertedValue() should return an instance of DateRange. To get dates from calendars, I would need to call getConvertedValue() on each but this method is protected and I cannot access it.
Do I need to manually get the pattern from the p:calendar and convert the value myself? If yes, how do I even know how the conversion is really done in Calendaritself? I wonder how is this approached in more complex components where conversion on subcomponents is not that trivial.

Display list of images using p:graphicImage in p:dataGrid after uploading image through p:fileUpload

i am uploading multiple images through p:fileUpload and showing uploaded images in datagrid but after upload the blank panel is created inside datagrid
<p:panelGrid columns="1" layout="grid" style="width: 1000px">
<p:outputLabel value="Upload Images"></p:outputLabel>
<p:fileUpload mode="advanced" multiple="false"
allowTypes="/(\.|\/)(gif|jpe?g|png)$/" dragDropSupport="true"
update="messages,updimgsfields,carouselcomp" sizeLimit="100000"
fileUploadListener="#{drawingattachmnt.handleFileUpload}"></p:fileUpload>
<!-- <p:graphicImage id="gimPhoto" value="#{fileuploadSection.image}" /> -->
</p:panelGrid>
<p:fieldset id="updimgsfields" legend="Uploaded Images">
<p:dataGrid id="updimgsdatagrid" var="upldimg"
value="#{drawingattachmnt.uploadedimages}" columns="4">
<p:panel id="drawingattachmntpnl" header="#{upldimg.displayId}"
style="text-align:center">
<h:panelGrid columns="1" cellpadding="5"
style="width: 100%; height: 200px;">
<p:graphicImage value="#{upldimg.imgcontent}" cache="false"
stream="true">
<f:param id="imgId" name="photo_id" value="#{upldimg.id}" />
</p:graphicImage>
</h:panelGrid>
</p:panel>
<p:draggable for="drawingattachmntpnl" revert="true"
handle=".ui-panel-titlebar" stack=".ui-panel" />
</p:dataGrid>
</p:fieldset>
// file upload function inside bean
public void handleFileUpload(final FileUploadEvent event) throws IOException {
if (event.getFile().getContents() != null) {
final ByteArrayOutputStream byteArrOutputStream = new ByteArrayOutputStream();
BufferedImage uploadedImage = null;
byte[] imageBytes = null;
try {
final String imageType = event.getFile().getContentType() != null
|| event.getFile().getContentType().split("/") != null ? event.getFile().getContentType()
.split("/")[1] : "jpeg";
uploadedImage = ImageIO.read(event.getFile().getInputstream());
ImageIO.write(uploadedImage, imageType, byteArrOutputStream);
imageBytes = byteArrOutputStream.toByteArray();
updimg.setImgcontent(new DefaultStreamedContent(new ByteArrayInputStream(imageBytes), imageType));
updimg.setId(UUID.randomUUID().toString().substring(0, 8));
updimg.setDisplayId("FIG: ");
uploadedimages.add(updimg);
} catch (final IOException io) {
} finally {
try {
byteArrOutputStream.close();
// imageInputStream.close();
} catch (final IOException e1) {
e1.printStackTrace();
}
uploadedImage.flush();
}
final FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage("Successful", "File uploaded Successfully "));
}
// List POJO
import org.primefaces.model.StreamedContent;
public class UploadImage {
public String displayId;
public String id;
public StreamedContent imgcontent;
public UploadImage() {
}
public UploadImage(final String id, final String displayId, final StreamedContent imgcontent) {
this.id = id;
this.displayId = displayId;
this.imgcontent = imgcontent;
}
#Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final UploadImage other = (UploadImage) obj;
if (this.id == null ? other.id != null : !this.id.equals(other.id)) {
return false;
}
return true;
}
public String getDisplayId() {
return displayId;
}
public String getId() {
return id;
}
public StreamedContent getImgcontent() {
return imgcontent;
}
#Override
public int hashCode() {
int hash = 7;
hash = 59 * hash + (this.id != null ? this.id.hashCode() : 0);
return hash;
}
public void setDisplayId(final String displayId) {
this.displayId = displayId;
}
public void setId(final String id) {
this.id = id;
}
public void setImgcontent(final StreamedContent imgcontent) {
this.imgcontent = imgcontent;
}
}
I need to show images in datagrid dynamically, but i am getting blank image panel inside datagrid. what should be done ?

How to refresh a custom component with primefaces update or jsf render? [duplicate]

This question already has an answer here:
Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?
(1 answer)
Closed 8 years ago.
i try to define a custom component with jsf2.1. The component must be update after button click. The component have a rendered attribute, default to false, that is update on the button click. This is my code.
<cu:ActionBar id="actionBar" rendered="#{bean.booleanCondition}">
<!-- some code inside !-->
</cu:ActionBar>
The code that trigger the update.
<p:commandButton id="myButton" actionListener="#{bean.foo} update=":actionBar"/>
with this code, bean.booleanCondition is set to true, but actionBar component is never display. If i wrap the component inside a it works.
this is my ActionBar component class :
#FacesComponent(value = "ACTION_BAR")
public class ActionBarComponent extends UIPanel {
#Override
public void encodeBegin(FacesContext ctx) throws IOException {
if (ctx == null) {
throw new NullPointerException();
}
ResponseWriter writer = ctx.getResponseWriter();
writer.startElement("div", this);
if (this.getAttributes().get("id") != null) {
writer.writeAttribute("id", this.getAttributes().get("id"), "id");
}
String styleClass = this.getAttributes().get("styleClass") != null ? "" : " " + this.getAttributes().get("styleClass");
writer.writeAttribute("class", "actionBar" + styleClass, null);
writer.startElement("form", this);
writer.writeAttribute("enctype", "application/x-www-form-urlencoded", null);
writer.writeAttribute("method", "post", null);
writer.writeAttribute("name", "actionBarForm", null);
if (this.getAttributes().get("id") != null) {
writer.writeAttribute("id", this.getAttributes().get("id") + ":" + this.getAttributes().get("id") + "Form", "id");
}
writer.writeAttribute("action", ctx.getExternalContext().getRequestContextPath() + ctx.getExternalContext().getRequestServletPath(), null);
writer.startElement("div", this);
writer.writeAttribute("class", "actionBarGroup", "class");
writer.startElement("span", this);
writer.append("Actions :");
writer.endElement("span");
}
#Override
public void encodeEnd(FacesContext ctx) throws IOException {
if (ctx == null) {
throw new NullPointerException();
}
ResponseWriter writer = ctx.getResponseWriter();
writer.endElement("div");
writer.endElement("form");
writer.endElement("div");
}
}
How could i make it work?
You can not update component with renderedattribute. Here you have explanation and solution: update attribute does not update component Generally, wrap up rendered component in <h:panelGroup> and update it.

Creating JSF 2.2 Custom Component Dynamically

I've written (well, modified) a custom component for radio buttons appearing inside a datatable based on this article. I've modified it slightly for JSF 2.2 by using the #FacesRenderer and #FacesComponent tags and omitting the custom tag class. Everything works fine in my XHTML page
xmlns:custom="http://mynamespace"
...
<h:dataTable id="mySampleTable3"
value="#{myBackingBean.sampleTable3}"
var="item"
width="100%"
border="1"
first="0">
<f:facet name="header">
<h:panelGrid id="gridHeader3" columns="2" width="100%" >
<h:outputText id="outputText3"
value="Radios grouped in row(With our custom tag)"/>
</h:panelGrid>
</f:facet>
<h:column>
<f:facet name="header">
<h:outputText value="Emp Name" />
</f:facet>
<h:outputText id="empName" value="#{item.empName}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Excellent" />
</f:facet>
<custom:customradio id="myRadioId2"
name="myRadioRow"
value="#{item.radAns}"
itemValue="E" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Good" />
</f:facet>
<custom:customradio id="myRadioId3"
name="myRadioRow"
value="#{item.radAns}"
itemValue="G" />
</h:column>
...
</h:dataTable>
and the datatable of radio buttons work correctly (they are grouped by row). The problem arises when I use this component dynamically. So I have
HtmlPanelGrid radioGrid = (HtmlPanelGrid) getApplication()
.createComponent(HtmlPanelGrid.COMPONENT_TYPE);
...
UICustomSelectOneRadio customSelectOneRadio = (UICustomSelectOneRadio) getApplication().
createComponent(UICustomSelectOneRadio.COMPONENT_TYPE);
customSelectOneRadio.setId("rb_" + question.getQuestionId() + "_" + row + "_" + col);
customSelectOneRadio.setName("myRadioRow");
String binding = "#{" + beanName + "." + propName +
"[\"" + question.getQuestionId().toString() + "_" +
subQuestion.getId() + "\"]}";
ValueExpression ve = getExpressionFactory().
createValueExpression(getELContext(), binding, String.class);
customSelectOneRadio.setValueExpression("value", ve);
customSelectOneRadio.setItemValue(value);
radioGrid.getChildren().add(customSelectOneRadio);
Unfortunately, although the datatable is rendered, the radio buttons aren't. Blank columns appear. I was wondering if anyone knows if there is some method I should be overriding in my renderer. Mine looks like:
#FacesRenderer(componentFamily = UICustomSelectOneRadio.COMPONENT_FAMILY, rendererType = HTMLCustomSelectOneRadioRenderer.RENDERER_TYPE)
public class HTMLCustomSelectOneRadioRenderer extends Renderer {
public static final String RENDERER_TYPE = "com.myapp.jsf.renderers.HTMLCustomSelectOneRadioRenderer";
#Override
public void decode(FacesContext context, UIComponent component) {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
UICustomSelectOneRadio customSelectOneRadio;
if (component instanceof UICustomSelectOneRadio) {
customSelectOneRadio = (UICustomSelectOneRadio) component;
} else {
return;
}
Map map = context.getExternalContext().getRequestParameterMap();
String name = getName(customSelectOneRadio, context);
if (map.containsKey(name)) {
String value = (String) map.get(name);
if (value != null) {
setSubmittedValue(component, value);
}
}
}
private void setSubmittedValue(UIComponent component, Object obj) {
if (component instanceof UIInput) {
((UIInput) component).setSubmittedValue(obj);
}
}
private String getName(UICustomSelectOneRadio customSelectOneRadio,
FacesContext context) {
UIComponent parentUIComponent
= getParentDataTableFromHierarchy(customSelectOneRadio);
if (parentUIComponent == null) {
return customSelectOneRadio.getClientId(context);
} else {
if (customSelectOneRadio.getOverrideName() != null
&& customSelectOneRadio.getOverrideName().equals("true")) {
return customSelectOneRadio.getName();
} else {
String id = customSelectOneRadio.getClientId(context);
int lastIndexOfColon = id.lastIndexOf(":");
String partName = "";
if (lastIndexOfColon != -1) {
partName = id.substring(0, lastIndexOfColon + 1);
if (customSelectOneRadio.getName() == null) {
partName = partName + "generatedRad";
} else {
partName = partName + customSelectOneRadio.getName();
}
}
return partName;
}
}
}
private UIComponent getParentDataTableFromHierarchy(UIComponent uiComponent) {
if (uiComponent == null) {
return null;
}
if (uiComponent instanceof UIData) {
return uiComponent;
} else {
//try to find recursively in the Component tree hierarchy
return getParentDataTableFromHierarchy(uiComponent.getParent());
}
}
#Override
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
UICustomSelectOneRadio customSelectOneRadio
= (UICustomSelectOneRadio) component;
if (component.isRendered()) {
ResponseWriter writer = context.getResponseWriter();
writer.startElement("input", component);
writer.writeAttribute("type", "radio", "type");
writer.writeAttribute("id", component.getClientId(context), "id");
writer.writeAttribute("name", getName(customSelectOneRadio, context), "name");
if (customSelectOneRadio.getStyleClass() != null && customSelectOneRadio.getStyleClass().trim().
length() > 0) {
writer.writeAttribute("class", customSelectOneRadio.getStyleClass().trim(), "class");
}
if (customSelectOneRadio.getStyle() != null && customSelectOneRadio.getStyle().trim().length() > 0) {
writer.writeAttribute("style", customSelectOneRadio.getStyle().trim(), "style");
}
...
if (customSelectOneRadio.getValue() != null
&& customSelectOneRadio.getValue().equals(customSelectOneRadio.getItemValue())) {
writer.writeAttribute("checked", "checked", "checked");
}
if (customSelectOneRadio.getItemLabel() != null) {
writer.write(customSelectOneRadio.getItemLabel());
}
writer.endElement("input");
}
}
}
To quickly summarise, this is working when I use a custom tag in an facelets/XHTML file, but not when I use it dynamically, e.g.
<h:panelGroup id="dynamicPanel" layout="block" binding="#{documentController.dynamicPanel}"/>
If anyone has any ideas on what I could be missing I'd be grateful.
Gotcha! It all revolved around changing the rendererType property. From the Renderer class above, you can see I had it set to my own, i.e.
public static final String RENDERER_TYPE = "com.myapp.jsf.renderers.HTMLCustomSelectOneRadioRenderer";
(1) I changed it to
public static final String RENDERER_TYPE = "javax.faces.Radio";
(2) I also changed my xxx.taglib.xml file to reflect this, e.g.
<namespace>http://mycomponents</namespace>
<tag>
<tag-name>customradio</tag-name>
<component>
<component-type>CustomRadio</component-type>
<renderer-type>javax.faces.Radio</renderer-type> /*<----- See Here */
</component>
</tag>
(3) But, here's the final step to note. I also had to create the following constructor in my UICustomSelectOneRadio component. It didn't work without this either.
public UICustomSelectOneRadio() {
this.setRendererType("javax.faces.Radio");
}
Remember - I extended my component from UIInput, so javax.faces.Text would have been set. Leaving my component family as "CustomRadio" was fine.
I'm still not 100% comfortable with renderer types etc., but for anyone who wants a little info, BalusC explains it well here.

Find component by ID in JSF

I want to find some UIComponent from managed bean by the id that I have provided.
I have written the following code:
private UIComponent getUIComponent(String id) {
return FacesContext.getCurrentInstance().getViewRoot().findComponent(id) ;
}
I have defined a p:inputTextarea as:
<p:inputTextarea id="activityDescription" value="#{adminController.activityDTO.activityDescription}" required="true" maxlength="120"
autoResize="true" counter="counter" counterTemplate="{0} characters remaining." cols="80" rows="2" />
Now if a call to the method as getUIComponent("activityDescription") it is returning null, but if I call it as getUIComponent("adminTabView:activityForm:activityDescription") then I can get the org.primefaces.component.inputtextarea.InputTextarea instance.
Is there any way to get the component with only the id i.e., "activityDescription" not the absolute id i.e., "adminTabView:activityForm:activityDescription"?
You can use the following code:
public UIComponent findComponent(final String id) {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
final UIComponent[] found = new UIComponent[1];
root.visitTree(new FullVisitContext(context), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent component) {
if (component != null
&& id.equals(component.getId())) {
found[0] = component;
return VisitResult.COMPLETE;
}
return VisitResult.ACCEPT;
}
});
return found[0];
}
This code will find only the first component in the tree with the id you pass. You will have to do something custom if there are 2 components with the same name in the tree (this is possible if they are under 2 different naming containers).
I try this code, and it's help:
private static UIComponent getUIComponentOfId(UIComponent root, String id){
if(root.getId().equals(id)){
return root;
}
if(root.getChildCount() > 0){
for(UIComponent subUiComponent : root.getChildren()){
UIComponent returnComponent = getUIComponentOfId(subUiComponent, id);
if(returnComponent != null){
return returnComponent;
}
}
}
return null;
}
Thanks
Maybe it's not possible. The FacesContext.getCurrentInstance().getViewRoot().findComponent(id) method returns only one UIComponent. The ViewRoot is constructed as a tree so if you have two forms in the view, each one with a component with id="text", they will have it's parent components added to the id so they won't conflict. If you put the two id="text" components within the same form, you will have java.lang.IllegalStateException thrown.
If you want to find all components with the searched id, you could write a method that implements:
List<UIComponent> foundComponents = new ArrayList();
for(UIComponent component: FacesContext.getCurrentInstance().getViewRoot().getChildren()) {
if(component.getId().contains("activityDescription")){
foundComponents.add(component);
}
}
Or if you want to find the first occurrence:
UIComponent foundComponent;
for(UIComponent component: FacesContext.getCurrentInstance().getViewRoot().getChildren()) {
if(component.getId().contains("activityDescription")){
foundComponent = component;
break;
}
}
Just put prependId="false" to your form in which this textarea is.
Yes, in all parent components which are NamingContainers you have to add attribute prependId="false" - it will works in <h:form> for sure and should work in others. If it is not possible to set it via attribute in .xhtml file you have to set such value programaticaly.
Suggestion after question's author comment:
If there is not such attribute in components that you are using try write find method like this:
private UIComponent findComponent(String id, UIComponent where) {
if (where == null) {
return null;
}
else if (where.getId().equals(id)) {
return where;
}
else {
List<UIComponent> childrenList = where.getChildren();
if (childrenList == null || childrenList.isEmpty()) {
return null;
}
for (UIComponent child : childrenList) {
UIComponent result = null;
result = findComponent(id, child);
if(result != null) {
return result;
}
return null;
}
Next just invoke
UIComponent iamLookingFor = findComponent(myId, FacesContext.getCurrentInstance().getViewRoot());
That will help?

Resources