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.
Related
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;
}
}
From one of my previous questions on this site I realized I don't know anything about custom converter life cycle. I searched a bit on internet and found nothing useful.
I would like to understand if custom converters are created once for all and recycled any time they are needed or if they are created on the fly and destroyed.
I suppose their main purpose is to perform some easy and light tasks, so it would make no difference if the a new instance of the converter is created each time it is found inside a jsf page sent to the user.
But I would like to use a custom converter to solve a common task in what it is an unpaved way. My custom convert would have an heavy initialization logic, so I have to be sure about its life-cycle. It must be created once for all and not every time it is needed. Is it possible ?
Depending on the answers I will receive I can abort the idea of using custom converter or decide to move the heavy initialization logic in a singletone.
Converters are created once for each time you reference them when using #FacesConverter annotation. That means if you execute slow code there it'll bring you into problems.
Alternatively, you can annotate them as #ManagedBean with the scope you want and use them with an EL reference instead of raw converter id. If you want to initialize them in some way, the solution for you would be setting them the scope for the whole application and making them eagerly initialized, so they'll be created when application starts up:
Converter:
#ManagedBean(eager = true)
#ApplicationScoped
public class WorkerConverter implements Converter {
public WorkerConverter() {
System.out.println("Building converter...");
}
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
Integer id = Integer.parseInt(value);
if (id == 1) {
return new Worker(1, "John");
} else {
return new Worker(1, "Larry");
}
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
return ((Worker) value).getId().toString();
}
}
Managed bean:
#ManagedBean
#ViewScoped
public class SelectWorkerBean {
public static class Worker {
Integer id;
String name;
public Worker(Integer id, String name) {
this.id = id;
this.name = name;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Worker other = (Worker) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
#Override
public int hashCode() {
return id;
}
#Override
public String toString() {
return "Worker [name=" + name + "]";
}
}
private Worker selectedWorker;
private List<Worker> workers = Arrays.asList(new Worker(1, "John"),
new Worker(2, "Larry"));
public Worker getSelectedWorker() {
return selectedWorker;
}
public List<Worker> getWorkers() {
return workers;
}
public void send() {
System.out.println(selectedWorker + " selected");
}
public void setSelectedWorker(Worker selectedWorker) {
this.selectedWorker = selectedWorker;
}
}
Page:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Test page</title>
</h:head>
<h:body>
<h:form>
<h:selectOneMenu value="#{selectWorkerBean.selectedWorker}"
converter="#{workerConverter}">
<f:selectItems value="#{selectWorkerBean.workers}" var="worker"
itemLabel="#{worker.name}" />
</h:selectOneMenu>
<h:commandButton value="send" action="#{selectWorkerBean.send}" />
</h:form>
</h:body>
</html>
in my preRender code for a page i add faces message then make navigation to another page as follows:
if(error){
addMessageToComponent(null,"AN ERROR HAS OCCURRED");
FacesContext.getCurrentInstance().getExternalContext().getFlash()
.setKeepMessages(true);
navigateActionListener("myoutcome");
}
and the util methods for adding message and navigation are:
public static String getClientId(String componentId)
{
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
UIComponent c = findComponent(root, componentId);
return c.getClientId(context);
}
public static UIComponent findComponent(UIComponent c, String id)
{
if (id.equals(c.getId())) { return c; }
Iterator<UIComponent> kids = c.getFacetsAndChildren();
while (kids.hasNext())
{
UIComponent found = findComponent(kids.next(), id);
if (found != null) { return found; }
}
return null;
}
/**
* #param componentId
* : the id for the jsf/primefaces component without formId:
* prefix. <br>
* if you use null then the message will be added to the
* h:messages component.
**/
public static void addMessageToComponent(String componentId, String message)
{
if (componentId != null)
componentId = GeneralUtils.getClientId(componentId);
FacesContext.getCurrentInstance().addMessage(componentId,
new FacesMessage(message));
}
public static void navigateActionListener(String outcome)
{
FacesContext context = FacesContext.getCurrentInstance();
NavigationHandler navigator = context.getApplication()
.getNavigationHandler();
navigator.handleNavigation(context, null, outcome);
}
but messages are not saved and so it doesn't appear after redirect.
please advise how to fix that.
The preRenderView event runs in the very beginning of the RENDER_RESPONSE phase. It's too late to instruct the Flash scope to keep the messages. You can do this at the latest during the INVOKE_APPLICATION phase.
Since there's no standard JSF component system event for this, you'd need to homebrew one:
#NamedEvent(shortName="postInvokeAction")
public class PostInvokeActionEvent extends ComponentSystemEvent {
public PostInvokeActionEvent(UIComponent component) {
super(component);
}
}
To publish this, you need a PhaseListener:
public class PostInvokeActionListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.INVOKE_APPLICATION;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().publishEvent(context, PostInvokeActionEvent.class, context.getViewRoot());
}
}
After registering it as follows in faces-config.xml:
<lifecycle>
<phase-listener>com.example.PostInvokeActionListener</phase-listener>
</lifecycle>
You'll be able to use the new event as follows:
<f:event type="postInvokeAction" listener="#{bean.init}" />
You only need to make sure that you've at least a <f:viewParam>, otherwise JSF won't enter the invoked phase at all.
The JSF utility library OmniFaces already supports this event and the preInvokeAction event out the box. See also the showcase page which also demonstrates setting a facesmessage for redirect.
This Question may be asked in several threads...but could not fine the correct answer
a Java Bean
package com.example;
public class Document {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
An ArrayList creation of JavaBean as displayed below
package com.example;
import java.util.ArrayList;
public class classdocs {
public ArrayList getData() {
ArrayList docsmx = new ArrayList();
Document d1 = new Document();
d1.setName("user.doc");
Document d2 = new Document();
d2.setName("office.doc");
Document d3 = new Document();
d3.setName("transactions.doc");
docsmx.add(d1);
docsmx.add(d2);
docsmx.add(d3);
return docsmx;
}
}
an Action Class
package com.example;
import java.util.ArrayList;
import com.opensymphony.xwork2.ActionSupport;
public class FetchAction extends ActionSupport {
private String username;
private String message;
private ArrayList docsmx = new ArrayList();
public ArrayList getDocuments() {
return docsmx;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String execute() {
classdocs cx = new classdocs();
if( username != null) {
docsmx = cx.getData();
return "success";
} else {
message="Unable to fetch";
return "failure";
}
}
}
Jsp with Struts2 Iterator Tag
Documents uploaded by the user are:</br>
<s:iterator value="docsmx">
<s:property value="name" /></br>
</s:iterator>
Question Why the ArrayList of Bucket containing JavaBean not displayed when Iterated ...
Am I doing some thing wrong ???
with regards
karthik
Depending your version, you should either provide a getter for docsmx (preferred, pre-S2.1.mumble), or make docsmx public (not as preferred, S2.1+).
Or, based on your code, use the correct getter:
<s:iterator value="documents">
<s:property value="name" /></br>
</s:iterator>
A couple of notes: documents should likely be declared a List, not ArrayList, although in this case it almost certainly doesn't matter. It's a good habit to get in to, though, coding to an interface when the implementation doesn't matter.
I'd also consider tightening up the code a little bit:
public String execute() {
if (username == null) {
message = "Unable to fetch";
return "failure";
}
docsmx = cs.getData();
return "success";
}
This allows a more natural reading, makes it more clear what the two states are (success and failure), keeps them very distinct, and slightly shorter.
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.