radiogroup with tooltip not working properly - xpages

I have the same situation as one of previous questions :
checkboxgroup returning only last selected value
But this time only 1 value can be selected from the same data ,so I thought using a radio instead of a checkbox.
Values are comming from the repeat from the viewScope choices
<xp:repeat id="repeat4" rows="100" value="#{viewScope.choices}"
indexVar="rownumber" var="row" first="0">
<xp:radio id="radio1" groupName="selection">
<xp:this.text><![CDATA[#{javascript:row[3]}]]></xp:this.text>
<xp:this.value><![CDATA[#{viewScope.selected[rownumber]}]]></xp:this.value>
</xp:radio>
<xe:tooltip id="tooltip3" for="radio1">
<xe:this.label><![CDATA[#{javascript:return viewScope.choices[rownumber].get(3)}]]></xe:this.label>
</xe:tooltip>
</xp:repeat>
The tooltip seems correct but :
1) I can select all the radio boxes if I want instead of only one
2) data doesn't seem to update (so I don't know which box has been selected)

Add this onClientLoad CSJS code to your XPage:
<xp:eventHandler
event="onClientLoad"
submit="false">
<xp:this.script><![CDATA[
dojo.query("input").forEach(function(node){
var attr = node.getAttributeNode("name");
if (attr.value.indexOf(':selection') >= 0) {
attr.value = 'selection';
}
});
]]></xp:this.script>
</xp:eventHandler>
It "repairs" the name attribute of input elements so they get all the same name "selection" without the "repeat"-part:
from
to

Are you able to use a radio group?
<xp:radioGroup id="radioGroup1"></xp:radioGroup>

Question 1:
UPDATE:
You have to calculate a unique groupName for each row of your repeat control.
You have to calculate a common groupName for all of your radio buttons.
And there is the problem, each xp:radio control will be rendered with his own groupname, lets take a look at the rendered html code:
One possible workaround could be to work with native html:
<input id="radio1" type="radio" name="selection" value="#{viewScope.selected[rownumber]}">
</input>
If you need the full functionality of the xp:radio button core control then another option could be to rename the name property of your xp:radio buttons after rendering (e.g. with client side javascript).
Question 2:
I use a valueChangeListener for this purpose.
Example (from my own project):
XPage:
<xp:repeat var="entry" rows="30" value="#{accessBean.entries}" removeRepeat="true" repeatControls="false">
<xp:radio id="rbReadAccess" rendered="#{javascript:docData.isEditable()}"
title="Set read access" value="#{entry.accessLevelAsString}" selectedValue="READACCESS"
groupName="#{entry.name}" valueChangeListener="#{accessBean.accessLevelChanged}">
<xp:eventHandler event="onchange" submit="true" refreshMode="partial"
refreshId="pnlAccessControl">
</xp:eventHandler>
</xp:radio>
<xp:radio id="rbEditAccess" rendered="#{javascript:docData.isEditable()}"
title="Set edit access" value="#{entry.accessLevelAsString}" selectedValue="EDITACCESS"
groupName="#{entry.name}" valueChangeListener="#{accessBean.accessLevelChanged}">
<xp:eventHandler event="onchange" submit="true" refreshMode="partial"
refreshId="pnlAccessControl">
</xp:eventHandler>
</xp:radio>
<xp:radio id="rbFullAccess" rendered="#{javascript:docData.isEditable()}"
title="Set full access" value="#{entry.accessLevelAsString}" selectedValue="FULLACCESS"
groupName="#{entry.name}" valueChangeListener="#{accessBean.accessLevelChanged}">
<xp:eventHandler event="onchange" submit="true" refreshMode="partial"
refreshId="pnlAccessControl">
</xp:eventHandler>
</xp:radio>
</xp:repeat>
AccessControlBean:
public class AccessControlBean implements Serializable {
public Set<AccessControlEntry> getEntries() {
Set<AccessControlEntry> entries = new TreeSet<AccessControlEntry>(accessControlUserService.getEntries());
entries.addAll(accessControlGroupService.getEntries());
return entries;
}
public void accessLevelChanged(ValueChangeEvent valueChangeEvent) {
XspInputRadio component = (XspInputRadio) valueChangeEvent.getComponent();
System.out.println("phaseId: " + valueChangeEvent.getPhaseId());
System.out.println("accessLevelChanged");
System.out.println(component.getGroupName());
System.out.println("oldValue: " + valueChangeEvent.getOldValue());
System.out.println("newValue: " + valueChangeEvent.getNewValue());
}
}
AccessControlEntry:
public class AccessControlEntry implements Comparable<AccessControlEntry> {
// Enumerations
public static enum AccessLevel {
READACCESS,
EDITACCESS,
FULLACCESS;
}
// Fields
private String name;
private AccessLevel accessLevel;
private boolean accessLevelRestricted = false;
// Constructors
public AccessControlEntry(String name, AccessLevel accessLevel) {
this.name = name;
this.accessLevel = accessLevel;
}
// Getter & Setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public AccessLevel getAccessLevel() {
return accessLevel;
}
public void setAccessLevel(AccessLevel accessLevel) {
this.accessLevel = accessLevel;
}
// nessecary for EL (managed bean)
public String getAccessLevelAsString() {
return accessLevel.toString();
}
// nessecary for EL (managed bean)
public void setAccessLevelAsString(String accessLevel) {
this.accessLevel = AccessLevel.valueOf(accessLevel.toUpperCase());
}
// Operations
public boolean isReadAccess() {
return accessLevel == AccessLevel.READACCESS;
}
public void setEditAccess(boolean editAccessRestricted) {
accessLevel = AccessLevel.EDITACCESS;
accessLevelRestricted = editAccessRestricted;
}
public boolean isEditAccess() {
return accessLevel == AccessLevel.EDITACCESS;
}
public boolean isEditAccessRestricted() {
return isEditAccess() && accessLevelRestricted;
}
public void setFullAccess(boolean fullAccessRestricted) {
accessLevel = AccessLevel.FULLACCESS;
accessLevelRestricted = fullAccessRestricted;
}
public boolean isFullAccess() {
return accessLevel == AccessLevel.FULLACCESS;
}
public boolean isFullAccessRestricted() {
return isFullAccess() && accessLevelRestricted;
}
public int compareTo(AccessControlEntry aControlEntry) {
return getName().compareTo(aControlEntry.getName());
}
#Override
public String toString() {
return getClass().getSimpleName() + ": name=\"" + name +
"\", accessLevel=\"" + accessLevel +
"\", isEditAccessRestricted=\"" + isEditAccessRestricted() +
"\", isFullAccessRestricted=\"" + isFullAccessRestricted() + "\"";
}
}

Related

Xpages: 'RichText' field on web and saving with java bean

I have a requirement to allow users to enter 'RichText' in a field in an Xpages form on the web. It is just rich text (bold, size, color) no attachments/links/pictures. And no need to edit it on the client.
I have googled this and put together something that kind of is close, but doesn't work. I am only trying to get a minimal working example right now.
Used some info in this link and in another one I cannot find right now.
Any help would be greatly appreciated.
Xpage:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex">
<xp:messages id="messages1"></xp:messages>
<xp:panel id="pnlAll">
<xp:this.data>
<xe:objectData saveObject="#{javascript:docModel.save()}"
var="docModel">
<xe:this.createObject><![CDATA[#{javascript:var docModel = new com.scoular.model.Doc();
var unid = sessionScope.get("key");
if (unid != null) {
docModel.loadByUnid(unid);
sessionScope.put("key","");
viewScope.put("readOnly","Yes");
} else {
docModel.create();
viewScope.put("readOnly","No");
}
return docModel;}]]></xe:this.createObject>
</xe:objectData>
</xp:this.data>
<xp:inputRichText id="inputRichText1"
value="#{docModel.body}">
</xp:inputRichText>
<xp:button value="Save"
id="button1"
type="button"
styleClass="btn-primary">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete" save="true">
<xp:this.action><![CDATA[#{javascript:if (docModel.save() == true) {
}}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:panel>
</xp:view>
Java:
package com.scoular.model;
import java.io.Serializable;
import java.util.Date;
import org.openntf.domino.Database;
import lotus.domino.MIMEEntity;
import lotus.domino.Stream;
import org.openntf.domino.DateTime;
import org.openntf.domino.Document;
import org.openntf.domino.Session;
import org.openntf.domino.utils.Factory;
import org.openntf.domino.xsp.XspOpenLogUtil;
public class Doc implements Serializable {
private static final long serialVersionUID = -5867831497684227875L;
private com.ibm.xsp.http.MimeMultipart body;
// Common Fields
private String unid;
private Boolean newNote;
private DateTime crtDte;
private String crtUsr;
public Doc() {
}
public void create() {
try {
newNote = true;
Session session = Factory.getSession();
Date date = new Date();
crtDte = session.createDateTime(date);
crtUsr = session.getEffectiveUserName();
} catch (Exception e) {
XspOpenLogUtil.logError(e);
}
}
public void loadByUnid(String unid) {
try {
Session session = Factory.getSession();
String DataDBpath = session.getCurrentDatabase().getServer() + "!!" + "scoApps\\Spectrum\\cashmarkData.nsf";
Database DataDB = session.getDatabase(DataDBpath);
Document doc = DataDB.getDocumentByUNID(unid);
if (null == doc) {
System.out.println("Document not found");
} else {
loadValues(doc);
}
} catch (Exception e) {
XspOpenLogUtil.logError(e);
}
}
public void loadValues(Document doc) {
try {
// common fields
newNote = false;
unid = doc.getUniversalID();
crtDte = doc.getItemValue("checkInDate", DateTime.class);
crtUsr = doc.getItemValueString("crtUsr");
// custom fields
} catch (Exception e) {
XspOpenLogUtil.logError(e);
}
}
public boolean save() {
boolean tmpSave = true;
try {
Document doc = null;
Session session = Factory.getSession();
String DataDBpath = session.getCurrentDatabase().getServer() + "!!" + "scoApps\\Spectrum\\cashmarkData.nsf";
Database DataDB = session.getDatabase(DataDBpath);
if (newNote) {
doc = DataDB.createDocument();
doc.put("form", "doc");
} else {
doc = DataDB.getDocumentByUNID(unid);
}
//Create the body as a MIME entity
session.setConvertMIME(false); // Do not convert MIME to RT MIMEEntity body = doc.createMIMEEntity("body");
Stream stream = session.createStream();
stream.writeText("<ul><li>hello</li><li>world</li></ul>Google");
//body.setContentFromText(stream, "text/html;charset=UTF-8", MIMEEntity.ENC_IDENTITY_7BIT);
stream.close();
doc.save();
} catch (Exception e) {
XspOpenLogUtil.logError(e);
}
return tmpSave;
}
// Getters and Setters for common fields
public String getUnid() {
return unid;
}
public void setUnid(String unid) {
this.unid = unid;
}
public Boolean getNewNote() {
return newNote;
}
public void setNewNote(Boolean newNote) {
this.newNote = newNote;
}
public DateTime getCrtDte() {
return crtDte;
}
public void setCrtDte(DateTime crtDte) {
this.crtDte = crtDte;
}
public String getCrtUsr() {
return crtUsr;
}
public void setCrtUsr(String crtUsr) {
this.crtUsr = crtUsr;
}
public com.ibm.xsp.http.MimeMultipart getBody() {
return body;
}
public void setbody(com.ibm.xsp.http.MimeMultipart body) {
this.body = body;
}
}
To save your MimeMultipart to a MimeEntity field give this a try:
String fieldName = "yourFieldName";
if (doc.hasItem(fieldName))
doc.removeItem(fieldName);
Stream stream = session.createStream();
stream.writeText(body.getHTML());
MIMEEntity mimeEnt = doc.createMIMEEntity(fieldName);
mimeEnt.setContentFromText(stream,
"text/html;charset=UTF-8", MIMEntity.ENC_NONE);
stream.close();
stream.recycle();
Also you may want to remove some CKEditor plugins by adding a dojoAttribute called 'removePlugins'
<xp:inputRichText id="inputRichText1">
<xp:this.dojoAttributes>
<xp:dojoAttribute name="removePlugins"
value="ibmxspimage,smiley,ibmsametimeemoticons"></xp:dojoAttribute>
</xp:this.dojoAttributes>
</xp:inputRichText>
This will remove the toolbar buttons that allow the user to choose emoticons / upload images (which will cause an error)
Note however this will not prevent users from trying to paste Images into the CKEditor, to prevent that you need to make/obtain a custom CKEditor plugin that prevents pasting images both via a 'Data URL' (clipboard paste) or via 'Href' e.g. web address based images.

Interval Timer in XPages losing Date value on refresh

Hi I have a 'mail in' application and display the emails waiting in the queue (line) on a 'wallboard'. I wish to refresh the display every x seconds using a partial refresh of the repeat containing the mail data. This works fine other than the I have a problem with a date field, which displays correctly until the first refresh, when it converts to the default 1 Jan 1970. I use the created date to calculate the no. of minutes the mail has been waiting - not shown here.
Any help would be appreciated.
Many thanks
On the XPage I have:
<xp:scriptBlock
id="scriptBlockRefresh">
<xp:this.value>
<![CDATA[
setInterval(function() {
XSP.partialRefreshGet("#{id:mailInPanel}", {})
}, 5 * 1000)
]]>
</xp:this.value>
</xp:scriptBlock>
<xp:this.beforePageLoad><![CDATA[#{javascript:miniWallboardBean.loadOutstandingMailin();}]]></xp:this.beforePageLoad>
<xp:panel id="mailInPanel">
<xp:repeat id="repeat1" rows="30" value="#{miniWallboardBean.outstandingMailin}" var="rowData">
<xp:text escape="true" id="computedField1" value="#{rowData.from}"></xp:text>
<xp:text escape="true" id="computedField2" value="#{rowData.subject}"></xp:text>
<xp:text escape="true" id="computedField5" value="#{javascript:rowData.getCreatedDate().toJavaDate()}"></xp:text>
</xp:repeat>
</xp:panel>
In my Mailin Class
import lotus.domino.DateTime;
public class Mailin {
private String from;
private String subject;
private DateTime createdDate;
private String owner;
public String getFrom() {return from;}
public void setFrom(String from) {this.from = from;}
public String getSubject() {return subject;}
public void setSubject(String subject) {this.subject = subject;}
public DateTime getCreatedDate() {return createdDate;}
public void setCreatedDate(DateTime createdDate) {this.createdDate = createdDate;}
public String getOwner() {return owner;}
public void setOwner(String owner) {this.owner = owner;}
}
I get the data from view columns in my business logic:
public List <Mailin> getOutstandingMailin(){
ArrayList<Mailin> outstandingMailin = new ArrayList<Mailin>();
try {
ViewEntryCollection entries = NCLWallboardUtil.getAllEntries("Server","DB","View"); //Method to get a collection
if (entries !=null) {
ViewEntry entry = entries.getFirstEntry();
while (entry !=null){
Mailin mailin = loadMailInFromEntry(entry);
outstandingMailin.add(mailin);
ViewEntry oldEntry = entry;
entry = entries.getNextEntry(entry);
oldEntry.recycle();
}
entries.recycle();
}
} catch (NotesException e) {
e.printStackTrace();
}
return outstandingMailin;
}
private Mailin loadMailInFromEntry(ViewEntry entry) throws NotesException{
Mailin mailin = new Mailin();
mailin.setFrom((String) entry.getColumnValues().get(0));
mailin.setSubject((String) entry.getColumnValues().get(3));
mailin.setOwner((String) entry.getColumnValues().get(5));
mailin.setCreatedDate((DateTime) entry.getColumnValues().get(4));
return mailin;
}
In my session scope bean I have:
public class MiniWallboardBean implements Serializable{
private static final long serialVersionUID = 1L;
private List <Mailin> outstandingMailin;
private MiniWallboard miniWB;
public MiniWallboardBean(){
miniWB = new MiniWallboard();}
public void loadOutstandingMailin(){
try{
setOutstandingMailin(miniWB.getOutstandingMailin());
}catch (Exception e){
e.printStackTrace();
}
}
public void setOutstandingMailin(List <Mailin> outstandingMailil{
this.outstandingMailin = outstandingMailin;}
public List <Mailin> getOutstandingMailin() {
return outstandingMailin;}
DateTimes, like other Domino objects, are not serializable. So once the initially page load is completed and the Session recycled, your DateTime will also be recycled.
Store the values as Java Dates instead and you will be fine.

action p:command button doesn't set property into managed bean [duplicate]

This question already has answers here:
commandButton/commandLink/ajax action/listener method not invoked or input value not set/updated
(12 answers)
Closed 7 years ago.
I have an issue, I have a carousel, and I load dynamically some data into it, when I press into a carousel object it have to set a value into a manangedbean (CurrentSong) and show a dialog, now the dialog appear but the set property doesn't work. why?
xhtml page:
<h:body style="background: url(../resources/images/knapsack_background_light.jpg); background-attachment:fixed;">
<div id="contentContainer" class="trans3d">
<section id="carouselContainer" class="trans3d">
<ui:repeat value="#{retrieve.mostPopularSongs}" var="carouselSelectedSong">
<figure id="item" class="carouselItem">
<div class="itemInfo">
<h:commandButton id="selectedButton"
action="#{currentSong.setSong(carouselSelectedSong)}"
styleClass="btn"
onclick="parent.showSongDialog();"
style="
background-image: url('#{carouselSelectedSong.coverPath}');
background-size:100%;
width:300px;
height:300px;
border: black;">
<f:ajax render="songDialogContent"/>
</h:commandButton>
</div>
</figure>
</ui:repeat>
</section>
</div>
managed bean #ManagedBean #SessionScoped:
public class CurrentSong implements Serializable {
#EJB
private CustomerManagementLocal customerManagement;
#EJB
private SocialManagementLocal socialManagement;
private Customer customer;
private Song song;
private String textComment;
public CurrentSong() {
}
public Customer getCustomer() {
return customer;
}
public Song getSong() {
return song;
}
public void setSong(Song song) {
System.out.println("----------------------------------------- current song: " + song.getTitle());
this.song = song;
}
public void putLike () {
putValutation(true);
}
public void putDislike () {
putValutation(false);
}
public String getTextComment() {
return textComment;
}
public void setTextComment(String textComment) {
this.textComment = textComment;
}
public void putComment () {
FacesContext context = FacesContext.getCurrentInstance();
try {
Comment newComment = new Comment(customer, new Date(), textComment, song);
song.getCommentList().add(newComment);
socialManagement.putComment(newComment);
Notifier.notifyInfoMessage(context, Constants.INSERTION_COMMENT_SUCCESSFULLY);
RequestContext requestContext = RequestContext.getCurrentInstance();
requestContext.execute("clearTextComment();");
} catch (CustomerNotFoundException ex) {
Notifier.notifyErrorMessage(context, Constants.INTERNAL_ERROR);
} catch (SongNotFoundException ex) {
Notifier.notifyErrorMessage(context, Constants.INTERNAL_ERROR);
}
}
private void putValutation (boolean valutation) {
FacesContext context = FacesContext.getCurrentInstance();
try {
socialManagement.putValutation(new LikeValutation(customer, song, valutation, new Date()));
Notifier.notifyInfoMessage(context, Constants.INSERTION_VALUTATION_SUCCESSFULLY);
} catch (CustomerNotFoundException | SongNotFoundException ex) {
Notifier.notifyErrorMessage(context, Constants.INTERNAL_ERROR);
}
}
#PostConstruct
public void init() {
customer = customerManagement.getCurrentCustomer();
}
}
thanks!
Define one selectedSong variable in your managed bean with getters and setters.
Use JSF setPropertyActionListener similar to below code.
Remove the argument from your action method.
<h:commandButton id="selectedButton"
action="#{currentSong.setSong()}"
styleClass="btn"
onclick="parent.showSongDialog();"
style="
background-image: url('#{carouselSelectedSong.coverPath}');
background-size:100%;
width:300px;
height:300px;
border: black;">
<f:ajax render="songDialogContent"/>
<f:setPropertyActionListener target="#{currrentSong.selectedSong}" value="#{carouselSelectedSong}" />
</h:commandButton>
Assign the selectedSong to carouselSelectedSong in your managed bean action class
private Song selectedSong;
//getters and setters for selectedSong
public String setSong() {
System.out.println("----------------------------------------- current song: " + selectedSong.getTitle());
this.song = selectedSong;
return null;
}
I think the method setSong is not executed, that is the problem.
public void setSong(Song song) {
System.out.println("----------------------------------------- current song: " + song.getTitle());
this.song = song;
}
Normally the action method expects the method should return the String return value. Can you change the method definition like below code then it will work
public String setSong(Song song) {
System.out.println("----------------------------------------- current song: " + song.getTitle());
this.song = song;
return null;
}

Populating selectItems of the combobox (label, value) using a managed bean

I have a combo in my page which I want to have populated with some keywords from configuration. I want to use a managed bean to accomplish it.
Let's say that I have a bean called Config, where there is a List categories field. ..
public class Configuration implements Serializable {
private static final long serialVersionUID = 1L;
private List<String> categories;
public List<String> getCategories() {
if (categories == null)
categories = getCats();
return categories;
}
//... etc.
}
When I use this field for my combo, it works well...
<xp:comboBox>
<xp:selectItems>
<xp:this.value><![CDATA[#{config.categories}]]></xp:this.value>
</xp:selectItems>
</xp:comboBox>
But, it's only a list of labels. I need values, too. How do I populate selectItems of my combo with TWO strings - a label and a value?
EDIT:
I tried to create an object Combo with label and value fields and use a repeat inside my comboBox.
<xp:comboBox>
<xp:repeat id="repeat1" value="#{config.combo}" var="c" rows="30">
<xp:selectItem itemLabel="#{c.label}" itemValue="#{c.value}" />
</xp:repeat>
</xp:comboBox>
Still not working... :-(
Instead of returning a List<String> your function should return a List<javax.faces.model.SelectItem>. Here's a sample:
public static List<SelectItem> getComboboxOptions() {
List<SelectItem> options = new ArrayList<SelectItem>();
SelectItem option = new SelectItem();
option.setLabel("Here's a label");
option.setValue("Here's a value");
options.add(option);
return options;
}
Advantage of using this method (besides not having to use that nonconceptual stuff :-) is that you can also the SelectItemGroup class to group the options:
public static List<SelectItem> getGroupedComboboxOptions() {
List<SelectItem> groupedOptions = new ArrayList<SelectItem>();
SelectItemGroup group = new SelectItemGroup("A group of options");
SelectItem[] options = new SelectItem[2];
options[0] = new SelectItem("here's a value", "here's a label");
options[1] = new SelectItem("here's a value", "here's a label");
group.setSelectItems(options);
groupedOptions.add(group);
return groupedOptions;
}
You can use SelectItems. (see http://docs.oracle.com/javaee/6/api/javax/faces/model/SelectItem.html)
You can specify both value and label, or value only.
import javax.faces.model.SelectItem;
public List<SelectItem> getCategories() {
try {
ArrayList<SelectItem> ret = new ArrayList<SelectItem>();
ret.add(new SelectItem("my value", "my label"));
return ret;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

JSF MethodExpression isn't triggering my backing bean action

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));

Resources