I have situation here. I use managed bean as converter to pre populate SelectManyCheckBox from database and it works good. Here is my Converter class
#ManagedBean
#RequestScoped
public class RoleConverter implements Converter{
#EJB
private UserRoleSingleton userRoleSingleton;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if(value == null){
return null;
}
return userRoleSingleton.getUserRoleById(Integer.parseInt(value));
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if(!(value instanceof Userrole) || ((Userrole) value).getRoleId() == null){
return null;
}
Userrole role = (Userrole) value;
return String.valueOf(role.getRoleId());
}
public RoleConverter() {
}
}
After it preselect checkboxes I select or deselect check boxes or do nothing and click submit button in form.
Then button's first click just executes convertor's getAsString method again and reload page.
And on second click it executes getAsObject method as expected then action.
Here is my SelectManyCheckBox:
<p:selectManyCheckbox id="orgpermission"
value="#{adminView.selectedrolesorg}" layout="pageDirection">
<f:selectItems value="#{adminView.allRolesRelatedToOrgInfo}"
var="citrole" itemValue="#{citrole}"
itemLabel="#{citrole.roledescription}" converter="#{roleConverter}" />
</p:selectManyCheckbox>
Why does it executes getAsString method again and how to solve it to fire action method on first click?
Related
The JSF component uses binding and backing bean is of View scope. The component have validator and valueChangeListener set. And when component's value is changed partial request is sent to server. And validator and valueChangListener are called many times but not once within request.
How to make them to be called once during request?
If remove binding the methods are called correctly once.
But is it possible to do not remove binding and make listeners be called once?
Sample of used code is next:
<h:inputText id="txt"
validator="#{myBean.validateValue}"
valueChangeListener="#{myBean.valueChanged}"
binding="#{myBean.txtInput}">
<f:ajax event="valueChange"
execute="#this"
render="#this"/>
</h:inputText>
#ViewScoped
#ManagedBean
public class MyBean {
private HtmlInputText txtInput;
public void valueChanged(ValueChangeEvent ve) {
...
}
public void validateValue(FacesContext context, UIComponent component, Object value) {
...
}
public HtmlTextInput getTxtInput() {
return txtInput;
}
public void setTxtInput(HtmlTextInput txtInput) {
this.txtInput = txtInput;
}
}
The same issue takes place for actionListener and commandLink component if it uses binding and backing bean has View scope.
The possible solution could be just to override class which is used by UIInput and UICommand to store validators and listeners. The class is javax.faces.component.AttachedObjectListHolder.
This class straightforward add new listener to backing list not checking if the same listener already is there.
Thus the solution is to check if listener exists and not to add it then.
So take javax.faces.component.AttachedObjectListHolder from jsf-api-2.1.<x>-sources.jar and add it to your project to the corresponding package. Replace method add with such one:
void add(T attachedObject) {
boolean addAttachedObject = true;
if (attachedObject instanceof MethodExpressionActionListener
|| attachedObject instanceof MethodExpressionValueChangeListener
|| attachedObject instanceof MethodExpressionValidator) {
if (attachedObjects.size() > 0) {
StateHolder listener = (StateHolder) attachedObject;
FacesContext context = FacesContext.getCurrentInstance();
Object[] state = (Object[]) listener.saveState(context);
Class<? extends StateHolder> listenerClass = listener.getClass();
for (Object tempAttachedObject : attachedObjects) {
if (listenerClass.isAssignableFrom(tempAttachedObject.getClass())) {
Object[] tempState = (Object[]) ((StateHolder) tempAttachedObject).saveState(context);
if (((MethodExpression) state[0]).getExpressionString().equals(((MethodExpression)tempState[0]).getExpressionString())) {
addAttachedObject = false;
break;
}
}
}
}
}
clearInitialState();
if (addAttachedObject) {
attachedObjects.add(attachedObject);
}
}
After that validators, valueChangeListeners and actionListeners will be triggered only once even when component binding and "View", "Session" or "Application" scopes are used.
I wonder if should be possible to add an attribute to a component inside a converter? So inside the getAsString I would use uiComponent.addAttribute(). This seems to work 50% for me, the initial value is set, but when the converter is called later setting a new value the initial value is still retrieved.
you should not do it this way since it breaks separation of duties. you should use a bean or a scope attribute instead.
but maybe this suits:
<h:inputText value="#{bean.someValue}" converter="#{bean}">
<f:attribute name="attrName" value="#{bean.attrValue}"/>
</h:inputText>
and
#ManagedBean
public class Bean implements Converter
{
private String someValue;
private String attrValue;
#Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
attrValue = "uppercase";
return someValue.toUpperCase();
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
attrValue = "lowercase";
return value.toLowerCase();
}
public String getSomeValue()
{
return someValue;
}
public void setSomeValue(String someValue)
{
this.someValue = someValue;
}
public String getAttrValue()
{
return attrValue;
}
public void setAttrValue(String attrValue)
{
this.attrValue = attrValue;
}
}
I read on SO some QA about the same component, but I feel I'm missing something, because I am one step behind.
I can't even make the page open when using the primefaces autocomplete component in it.
The snippet for it is:
<p:autoComplete value="#{indirizzoCtrl.selectedCodiceNazione}"
completeMethod="#{indirizzoCtrl.completeNazione}"
var="nazione" itemLabel="#{nazione.nome}"
itemValue="#{nazione.codiceNazione}" />
Nazione is a Pojo class where CodiceNazione and Nome are two String field (with getter and setter for sure). completeNazione is a method on the ManagedBean that returns List<Nazione>.
Looking at BalusC explanation here, it seems to me that i don't need any converter involved, because both itemValue and value attributes are mapped to string property.
Anyway, when I just open the page containing this autocomplete snippet, it crashes with this error:
javax.el.PropertyNotFoundException: /Cliente/Indirizzo.xhtml #23,56 itemValue="#{nazione.codiceNazione}": itemValue="#{nazione.codiceNazione}": Property 'codiceNazione' not found on type java.lang.String
Why this is happening? I really can't get it. The method completeNazione hasn't even called yet, so it shouldn't know any Nazione yet.
What's wrong with it?
Edited:
Following the suggestion, I tried to add a converter, but I still get the same error.
Here's my converter:
public class NazioneConverter implements Converter {
final static Logger log = Logger.getLogger(NazioneConverter.class);
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value.trim().equals("")) {
return null;
} else {
try {
IndirizzoRepository ir = new IndirizzoRepository();
List<Nazione> nazioni = ir.getNazioneByName(value);
if (nazioni.size()==1) return nazioni.get(0);
else throw new Exception();
} catch (Exception e) {
String msg = "Errore di conversione";
log.error(msg, e);
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, "Non è una nazione conosciuta"));
}
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null || value.equals("")) {
return "";
} else {
return String.valueOf(((Nazione) value).getNome());
}
}
}
now the component in the view looks like:
<p:autoComplete value="#{indirizzoCtrl.indirizzo.nazione.codiceNazione}"
completeMethod="#{indirizzoCtrl.completeNazione}"
var="nazione" itemLabel="#{nazione.nome}" converter="#{nazioneConverter}"
itemValue="#{nazione.codiceNazione}" forceSelection="true" />
But still don't working. The converter is not even invoked: I registered it in my faces-config.xml file.
I also tried itemValue="#{nazione}" as in the primefaces showcase but the problem became the ItemLabel attribute, mapped to nazione.nome.
What am I doing wrong?
This worked for me:
//Converter
#FacesConverter(value="MarcaConverter")
public class MarcaConverter implements Converter{
MarcaDAO marcaDAO;
public Object getAsObject(FacesContext contet, UIComponent component, String value) {
if(value==null || value.equals(""))
return null;
try{
int id = Integer.parseInt(value);
return marcaDAO.findMarcaById(id);
}catch (Exception e) {
e.printStackTrace();
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Marca no válida", ""));
}
}
public String getAsString(FacesContext contet, UIComponent component, Object value) {
if(value==null || value.equals(""))
return null;
return String.valueOf(((Marca)value).getCodigoMarca());
}
}
//--------------------------------------
//Bean
#ManagedBean
#ViewScoped
public class MyBeans implements Serializable{
private Marca marca;
...
public Marca getMarca(){
return marca;
}
public void setMarca(Marca m){
marca=m;
}
...
public List<Marca> obtenerMarcasVehiculos(String s) {
List<Marca> marcas,smarcas=new ArrayList<Marca>();
try{
marcas= marcaDAO.findAllMarcas();
if(s.trim().equals("")) return marcas;
for(Marca m:marcas)
if (m.getNombreMarca().toString().contains(s) || m.getNombreMarca().toLowerCase().contains(s.toLowerCase())) {
smarcas.add(m);
}
return smarcas;
}catch(Exception e){
//JsfUtil.showFacesMsg(e,"Error al obtener las marcas de vehículos","",FacesMessage.SEVERITY_WARN);
e.printStackTrace();
JsfUtil.lanzarException(e);
return null;
}
}
//-----------------------------------------
//*.xhtml page
...
<p:autoComplete
id="cbxMarca" value="#{myBean.marca}" size="40"
converter="MarcaConverter"
completeMethod="#{myBean.obtenerMarcasVehiculos}"
var="m" itemLabel="#{m.nombreMarca}" itemValue="#{m}"
forceSelection="true" dropdown="true"
required="true" scrollHeight="200">
</p:autoComplete>
...
//-----------------------------------------
//Class Marca
public class Marca implements Serializable{
private static final long serialVersionUID = 1L;
private Integer codigoMarca;
private String nombreMarca;
...
Have you read the user guide? http://www.primefaces.org/documentation.html
I must say I have never used the autocomplete with pojo but from what I've read in the user guide, Çağatay Çivici says there:
Note that when working with pojos, you need to plug-in your own converter.
Here you can find out that a converter (PlayerConverter) is implemented even if player.name and of the other props are Strings.
I admit this is interesting and I'll do some research but I don't have the necessary time right now...
Change
converter="#{nazioneConverter}" to converter="nazioneConverter" in autocomplete
Change the itemValue from itemValue="#{nazione.codiceNazione}" to itemValue="#{nazione}" in autoComplete.
I have a dynamically created HtmlCommandLink with an ActionListener, but when I click on the link, the action listener method is not being called.
Code:
public HtmlPanelGroup getP() {
p = new HtmlPanelGroup();
FacesContext ctx = FacesContext.getCurrentInstance();
HtmlCommandLink l = new HtmlCommandLink();
l.setTitle("Goto");
l.setImmediate(true);
l.addActionListener(new ComponenetListener());
//new ListenerTest());//new MethodExpressionActionListener(methodExpression) );
l.setValue("Go");
p.getChildren().add(l);
return p;
}
and listener code is
#ManagedBean
#SessionScoped
public class ComponenetListener implements ActionListener{
public ComponenetListener() {
String s="sridhar";
}
#Override
public void processAction(ActionEvent event) throws AbortProcessingException {
UIComponent eventComponent = event.getComponent();
System.out.println("test");
String strEventCompID = eventComponent.getId();
throw new UnsupportedOperationException("Not supported yet.");
}
}
You must give all your dynamically created input and command components a fixed ID.
l.setId("yourID");
You also need to ensure that there's a <h:form> (or UIForm) component present as tree parent.
I'm using Primefaces' PickList and I can't make it work. My problem is Converter. I followed the directions of another post, but in vain.
Here is my facelet
<p:pickList value="#{customerBean.preferredCategories}" var="category"
itemLabel="#{category.description}" itemValue="#{category}" converter="#{categoryConverter}">
</p:pickList>
and here my custom converter
#FacesConverter(forClass=CategoryLevelView.class,value="categoryLevelConverter")
public class CategoryConverter implements Converter {
public String getAsString(FacesContext context, UIComponent component, Object value) {
return String.valueOf(((Category) value).getId());
}
public Object getAsObject(FacesContext arg0, UIComponent arg1, String value) {
Category category = new Category();
category.setId(Integer.parseInt(value));
return category;
}
}
Category is composed by an id (int) and a description (String)
I want both source and target Lists to display the description String, and the selected categories set as a List of Category in my bean. Both lists are correctly loaded in the bean and the DualListModel is populated in preferredCategories. The problem is the PickList is not even rendered. Nothing happens, no error displayed, the page just stops rendering when the turns arrives to PickList, and I think it's because a wrong usage of converter. Which would be a correct way to implement my this case?
Thanks.
I think
#FacesConverter(forClass=CategoryLevelView.class,value="categoryConverter")
public class CategoryConverter implements Converter {
should be
#FacesConverter(forClass=Category.class,value="categoryConverter")
public class CategoryConverter implements Converter {
Change value of forClass to Category.class.
And, you should not need to mention the value of converter attribute in <p:picklist.
This working without an ArrayIndexOutOfBounds exception.
#FacesConverter("PickListConverter")
public class PickListConverter implements Converter {
public Object getAsObject(FacesContext facesContext, UIComponent component, String submittedValue) {
PickList p = (PickList) component;
DualListModel dl = (DualListModel) p.getValue();
for (int i = 0; i < dl.getSource().size(); i++) {
if (dl.getSource().get(i).toString().contentEquals(submittedValue)) {
return dl.getSource().get(i);
}
}
for (int i = 0; i < dl.getTarget().size(); i++) {
if (dl.getTarget().get(i).toString().contentEquals(submittedValue)) {
return dl.getTarget().get(i);
}
}
return null;
}
public String getAsString(FacesContext facesContext, UIComponent component, Object value) {
PickList p = (PickList) component;
DualListModel dl = (DualListModel) p.getValue();
// return String.valueOf(dl.getSource().indexOf(value));
return value.toString();
}
}
In this line:
#FacesConverter(forClass=CategoryLevelView.class,value="categoryLevelConverter")
It looks like you're trying to set the converter id to categoryLevelConverter.
In this line of your Facelet:
converter="#{categoryConverter}"
The converter id does not match.
i made one simple converter and it works well with all values in Primefaces PickList:
#FacesConverter("PickListConverter")
public class PickListConverter implements Converter{
public Object getAsObject(FacesContext facesContext, UIComponent component, String submittedValue) {
PickList p = (PickList) component;
DualListModel dl = (DualListModel) p.getValue();
return dl.getSource().get(Integer.valueOf(submittedValue));
}
public String getAsString(FacesContext facesContext, UIComponent component, Object value) {
PickList p = (PickList) component;
DualListModel dl = (DualListModel) p.getValue();
return String.valueOf(dl.getSource().indexOf(value));
}
}
I do not know if you have solved your problem but if not, you can try this.
In the getAsObject method , what you are doing is creating a new category object and setting its id and returning it. I think what you should do here is get the category from the database with that id and then return that.