Converter wird bei AutoComplete bei itemSelect nicht ausgeführt

Hallo,

ich verwende von PrimeFaces das AutoComplete control. Der Benutzer gibt einen Buchstaben ein und es werden auto. alle gespeicherten Kategorienamen die mit dem Buchstaben anfangen angezeigt, was auch funktioniert.

Wählt der Benutzer aus den vorgeschlagenen Kategorienamen einen aus, muss das ausgewählte “item” in das passende Category Objekt Konvertiert werden. Das entspricht dem value="#{CreateCategoryBean.newCategory.parent}"

Nun habe ich das problem, dass der Converter jedesmal mal bei KeyUP ausgeführt wird, aber nicht wenn man ein vorgeschlagenes item auswählt.

Habt ihr vielleicht eine Idee was ich ändern muss? Also beim KeyUp Event soll der Converter nicht ausgeführt werden, nur wenn man ein item auswählt.

                        <!-- Uppercategory -->
                        <h:outputLabel for="parentCategory" value="#{msg['category.create.parent']}"/>
                        <p:autoComplete id="parentCategory" completeMethod="#{CreateCategoryBean.categories}" maxResults="20"
                            value="#{CreateCategoryBean.newCategory.parent}" forceSelection="true" >
                            <f:converter converterId="Get.Converter.CategoryStringConverter" />
                            <f:validator validatorId="Get.Validator.CategoryExistsValidator" />
                            <f:ajax event="itemSelect" render="categoryParentmessage submit" execute="@this"   />
                            <f:ajax event="keyup" render="categoryParentmessage submit" execute="@this"   />
                        </p:autoComplete>
                        <h:messages for="parentCategory" id="categoryParentmessage"/>

Die completeMethod gibt übrigens eine List zurück.

LG

Wieso eine Liste aus Strings? Wieso nutzt du nicht “var”, “itemlabel” und “itemValue”?
Dann hast du direkt das Objekt was auch convertiert werden kann. Mit
<p:ajax event="itemSelect" listener="#{autoCompleteBean.handleSelect}"/>
kannst du dann auf das SelectEvent reagieren, sollte in der Bean irgendwie so aussehen:

public void handleSelect(SelectEvent event) {  
    //Caste es zu dem was es wirklich ist .....
}  

Das von @lltodoll wird in der Doku gut beschrieben.

Danke, ich habe mich an dem Beispiel gehalten. Da wird das ganze auch mit einem Converter gemacht.

                        <h:outputLabel for="parentCategory" value="#{msg['category.create.parent']}"/>
                        <p:autoComplete id="parentCategory" completeMethod="#{CreateCategoryBean.categories}" maxResults="20"
                            value="#{CreateCategoryBean.newCategory.parent}"  var="p" itemLabel="#{p.name}" itemValue="#{p}"  >
                            <f:converter converterId="Get.Converter.CategoryStringConverter" />
                            <f:validator validatorId="Get.Validator.CategoryExistsValidator" />
                            <f:ajax event="itemSelect" render="categoryParentmessage submit" execute="@this"   />
                            <f:ajax event="keyup" render="categoryParentmessage submit" execute="@this"   />
                        </p:autoComplete>
                        <h:messages for="parentCategory" id="categoryParentmessage"/>

Der Converter gibt auch das richtige Objekt zurück, wenn man in die autoComplete TextBox den vollen Namen einer Category eingibt. Wählt man jedoch aus den vorgeschlagenen Categories eine aus, springt der Converter nicht an, weshalb auch nicht CreateCategoryBean.newCategory.parent gesetzt wird.

Wenn ich wie im PrimeFaces Beispiel <f:validator validatorId=„Get.Validator.CategoryExistsValidator“ /> durch <p:autoComplete converter=„CategoryString“ > ersetze erhalte ich die Meldung Expression Error: Named Object: CategoryString not found. (Wenn ich CategoryStringConverter nehme kommt auch diese Meldung)

Das Category Objekt hat übrigens die Eigenschaften Name (String) Parent (Category).

Mein Converter sieht so aus:

@FacesConverter("Get.Converter.CategoryStringConverter")
public class CategoryStringConverter implements Converter {
    public Object getAsObject(FacesContext facesContext, UIComponent component, String submittedValue) {
        if (submittedValue.trim().equals("")) {
            return null;
        } else {

            if (getCategoryEM() != null) {
                for (Category category : getCategoryEM().findAll()) {
                    if (category.getName().equals(submittedValue)) {
                        return category;
                    }
                }
                
            } else {
                throw new ConverterException(new Exception("getCategoryEM() is null"));
            }

            return null;
        }
    }
    public String getAsString(FacesContext facesContext, UIComponent component, Object value) {
        if (value == null || value.equals("")) {
            return "";
        } else {
            return String.valueOf(((Category) value).getName());
        }
    }
    public CategoryFacadeLocal getCategoryEM() {
        return Utility.lookup(CategoryFacade.class);
    }

Ich habe nochmal versucht das Beispiel von PrimeFaces http://www.primefaces.org/showcase/ui/autoCompletePojo.jsf 1:1 zu kopieren und lauffähig zu machen. Der gelieferte Beispielcode ist nämlich nicht kompilierbar!

Ich habe zusätzlich ein Feld players in der Klasse AutoCompleteBean anlegen müssen. Jetzt erhalte ich noch die Meldung Expression Error: Named Object: player not found. und ich glaube das liegt am

                        var="p" itemLabel="#{p.name}" itemValue="#{p}" converter="player" forceSelection="true"> ``` converter.

Wie kann ich das Problem aus der Welt schaffen?

Hi du,

also habs jetzt mal ohne CDI gemacht, aber so ungefair sollte es aussehen:

Die AutoCompleteBean:

 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;

import javax.faces.bean.ManagedBean;

/**
 *
 * @author martin
 */
@ManagedBean
@SessionScoped
public class AutoCompleteBean implements Serializable {

    private Player selectedPlayer1;
    private Player selectedPlayer2;
    private List<Player> players;


    @PostConstruct
    public void init() {
        players = PlayerConverter.playerDB;
    }
    
    public Player getSelectedPlayer1() {
        return selectedPlayer1;
    }

    public void setSelectedPlayer1(Player selectedPlayer1) {
        this.selectedPlayer1 = selectedPlayer1;
    }

    public Player getSelectedPlayer2() {
        return selectedPlayer2;
    }

    public void setSelectedPlayer2(Player selectedPlayer2) {
        this.selectedPlayer2 = selectedPlayer2;
    }

    public List<Player> completePlayer(String query) {
        List<Player> suggestions = new ArrayList<Player>();

        for (Player p : players) {
            if (p.getName().startsWith(query)) {
                suggestions.add(p);
            }
        }

        return suggestions;
    }
}

Der PLayerConverter

import java.util.List;  
import javax.faces.application.FacesMessage;  
import javax.faces.bean.ManagedBean;
  
import javax.faces.component.UIComponent;  
import javax.faces.context.FacesContext;  
import javax.faces.convert.Converter;  
import javax.faces.convert.ConverterException;  
  
@ManagedBean
public class PlayerConverter implements Converter {  
  
    public static List<Player> playerDB;  
  
    static {  
        playerDB = new ArrayList<Player>();  
  
        playerDB.add(new Player("Messi", 10));  
        playerDB.add(new Player("Bojan", 9));  
        playerDB.add(new Player("Henry", 14));  
        playerDB.add(new Player("Iniesta", 8));  
        playerDB.add(new Player("Villa", 7));  
        playerDB.add(new Player("Xavi", 6));  
        playerDB.add(new Player("Puyol", 5));  
        playerDB.add(new Player("Afellay", 20));  
        playerDB.add(new Player("Abidal", 22));  
        playerDB.add(new Player("Alves", 2));  
        playerDB.add(new Player("Pique", 3));  
        playerDB.add(new Player("Keita", 15));  
        playerDB.add(new Player("Busquets", 16));  
        playerDB.add(new Player("Adriano", 21));  
        playerDB.add(new Player("Valdes", 1));  
        playerDB.add(new Player("Thiago", 30));  
    }  
  
    public Object getAsObject(FacesContext facesContext, UIComponent component, String submittedValue) {  
        if (submittedValue.trim().equals("")) {  
            return null;  
        } else {  
            try {  
                int number = Integer.parseInt(submittedValue);  
  
                for (Player p : playerDB) {  
                    if (p.getNumber() == number) {  
                        return p;  
                    }  
                }  
  
            } catch(NumberFormatException exception) {  
                throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Not a valid player"));  
            }  
        }  
  
        return null;  
    }  
  
    public String getAsString(FacesContext facesContext, UIComponent component, Object value) {  
        if (value == null || value.equals("")) {  
            return "";  
        } else {  
            return String.valueOf(((Player) value).getNumber());  
        }  
    }  
}  ```

Die index.xhtml

```html
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:p="http://primefaces.org/ui"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form id="form">
            <p:growl id="messages" showDetail="true"/>

            <p:panel header="Players">
                <h:panelGrid columns="2" cellpadding="5">
                    <h:outputLabel value="Basic Pojo: " for="basicPojo" />
                    <p:autoComplete value="#{autoCompleteBean.selectedPlayer1}" 
                                    id="basicPojo" completeMethod="#{autoCompleteBean.completePlayer}"
                                    var="p" itemLabel="#{p.name}" itemValue="#{p}" converter="#{playerConverter}" forceSelection="true">

                        <f:facet name="itemtip">
                            <h:panelGrid  columns="2" cellpadding="5">
                                <h:outputText value="Name: " />
                                <h:outputText id="modelNo" value="#{p.name}" />

                                <h:outputText value="Number " />
                                <h:outputText id="year" value="#{p.number}" />

                                <h:outputText value="Position " />
                                <h:outputText value="#{p.position}"/>
                            </h:panelGrid>
                        </f:facet>

                    </p:autoComplete>
                </h:panelGrid>
            </p:panel>
        </h:form>

    </h:body>
</html>


Und dann mal kurz erklären:
Foto habe ich rausgenommen, da du ja noch keine hast. Die Converter deklariere ich immer gerne als ManagedBeans, da diese später dann noch anderen Controller injecten können. @PostConstruct wird aufgerufen, wenn die Bean erstellt wird.

Hallo,

danke für deine Ausführung. Ich habe den Converter zu einem ManagedBean gemacht, und den Validator und die

                            <f:ajax event="itemSelect" render="categoryParentmessage submit" execute="@this"   />
                            <f:ajax event="keyup" render="categoryParentmessage submit" execute="@this"   /> 

weg gelassen, damit geht es :slight_smile: