Primefaces 4: Programmatisches Menu


#1

Hi,

ich habe gerade ein Primefaces-Problem.
Ich habe ein Menü, welches dynamisch und damit programmatisch erzeugt werden muss. Dies passiert in folgender Methode:

        DefaultMenuItem stornieren = new DefaultMenuItem("Stornieren");
        stornieren.setDisabled(disabled);
        stornieren.setTitle("Stornieren ist nur möglich, wenn keine Sendungen zugeordnet sind");
        stornieren.setCommand("pretty:lkwListe");
        return stornieren;
    }```

Jetzt würde ich gerne mein DefaultMenuItem um einen ActionListener erweitern. Laut verschiedenen gefundenen Threads (Primefaces Forum, Stackoverflow) habe ich öfters dieses Konstrukt gefunden: 

// Source: http://forum.primefaces.org/viewtopic.php?f=3&t=19806#p62032
item.addActionListener(new MenuListener());

public class MenuListener implements ActionListener {

public void processAction(ActionEvent event)
        throws AbortProcessingException {
    System.out.println(event.toString());
    System.out.println("----------->" +event.getComponent().getId());
}

}```

Die Codevervollständigung zeigt mir diesen Aufruf (addActionListener) gar nicht an. Im weiteren Verlauf heisst es:

You can use MenuItem.setActionExpression(MethodExpression);

Auch Fehlanzeige. Mich beschleicht gerade der Verdacht, dass ich mal wieder etwas total offensichtliches übersehe, aber ich komme nicht drauf. Habt ihr eine Idee, wie ich das umsetzen kann?

Gruß,
Tim


#2

MenuItem statt DefaultMenuItem verwenden?

Edit:

Auch Gruß Tim :wink:


#3

Ist das mit dem addActionListener bzw setActionListener ein Versehen deinerseits?


#4

Hier hält es sich leider so: org.primefaces.model.menu.DefaultMenuItem implements org.primefaces.model.menu.MenuItem

 @Sym  ich verstehe gerade leider nicht, worauf du dich beziehst?

Ich bin mittlerweile etwas weiter gekommen. Bis Primefaces 3.5 erbte org.primefaces.component.menuitem.MenuItemvon javax.faces.component.UiCommand, diese bot “setAction”, addActionListener und setActionExpression an. Ab Primefaces 4.0 erbt org.primefaces.model.menu.MenuItem von keiner JSF Klasse mehr, die Implementierung wurde einfach umgestellt. Ich frage da mal an, das muss doch außer mir noch jemanden stören… ? Ich würde ja in die Dokumentation dafür gucken, aber mehr als ein kurzes Howto gibt es dafür nicht.

Edit: Bzw. ich gehe davon aus, dass .setCommand dem Attribut action aus dem Markup entspricht, ich suche halt noch nach einer Möglichkeit für actionListener

Gruß,
Tim


#5

Jap, genau das ist der Grund, warum ich noch nicht auf Primefaces 4.0 umgestellt habe. :smiley:
Habe das mal kurz angeschaut und hatte nach dem ich alles angepasst hatte ebenfalls das Problem mit dem ActionListener, dass dieser nicht mehr vorhanden war aber Zwecks Zeitmangel hab ich noch nicht weiter geschaut,.


#6

Es herrscht auch in anderen Foren große Verwirrung :smiley:

Aber der Kollege hier scheint soweit auf dem richtigen Dampfer zu sein: http://forum.primefaces.org/viewtopic.php?f=3&t=34560#p110471

You can try to use org.primefaces.component.menuitem.UIMenuItem:

Gut, dass das in der offiziellen Doku steht! hust

*** Edit ***

Noch ein kurzes Update, der Codeblock sieht bei mir jetzt so aus:

    private MenuElement buildStornierenItem(Boolean disabled) {
        UIMenuItem stornieren = new UIMenuItem();
        stornieren.setId("stornierenMenuItem");
        stornieren.setDisabled(disabled);
        stornieren.setTitle("Stornieren ist nur möglich, wenn keine Sendungen zugeordnet sind");
        stornieren.addActionListener(listener);
        stornieren.setActionExpression(buildMethodExpression("#{lkwDetailController.remove(lkwDetailController.current)}"));
        return stornieren;
    }

    private MethodExpression buildMethodExpression(String action) {
        final Class<?>[] paramTypes = new Class<?>[0];
        FacesContext fc = FacesContext.getCurrentInstance();
        ExpressionFactory ef = fc.getApplication().getExpressionFactory();
        return ef.createMethodExpression(fc.getELContext(), action, String.class, paramTypes);
    }

Hier kann ich auf dem altebekannten weg eine Action setzen und auch ActionListener anhängen. Wenn der PrimeFaces Renderer jetzt versucht das Menü zu rendern, bekomme ich:

java.lang.IllegalArgumentException: 6
	at javax.faces.component.UIComponentBase.validateId(UIComponentBase.java:542)
	at javax.faces.component.UIComponentBase.setId(UIComponentBase.java:363)
	at org.primefaces.model.menu.BaseMenuModel.generateUniqueIds(BaseMenuModel.java:54)
	at org.primefaces.model.menu.BaseMenuModel.generateUniqueIds(BaseMenuModel.java:42)
	at org.primefaces.model.menu.DefaultMenuModel.generateUniqueIds(DefaultMenuModel.java:28)
	at org.primefaces.component.menu.BaseMenuRenderer.encodeEnd(BaseMenuRenderer.java:102)

Die Ursache dafür:

[SPOILER]

    
    private List<MenuElement> elements;

    public BaseMenuModel() {
        elements = new ArrayList<MenuElement>();
    }

    public void addElement(MenuElement element) {
        elements.add(element);
    }
    
    public List<MenuElement> getElements() {
        return elements;
    }
    
    public void generateUniqueIds() {
        this.generateUniqueIds(getElements(), null);
    }
    
    private void generateUniqueIds(List<MenuElement> elements, String seed) {
        if(elements == null || elements.isEmpty()) {
            return;
        }
        
        int counter = 0;
        
        for(MenuElement element : elements) {
            String id = (seed == null) ? String.valueOf(counter++) : seed + "_" + counter++;
            element.setId(id);
            
            if(element instanceof MenuGroup) {                
                generateUniqueIds(((MenuGroup) element).getElements(), id);
            }
        }
    }
}

[/SPOILER]

Im BaseMenuModel werden ohne Rücksicht auf Verluste IDs generiert und über (eventuell schon gesetzte IDs) überbügelt. Die für mein UiMenuItem ausgewählte ID “6” schmeckt dem Validator in javax.faces.component.UIComponentBase aber überhaupt nicht, es gibt eine IllegalArgumentException. Ich sehe gerade keinen anderen Weg, als das DefaultMenuModel von Primefaces zu überschreiben und diese Methode zu ändern. Eventuell ist es hier aber sogar gerechtfertigt einen Bugreport einzureichen.

Noch ein Edit: Der Bugreport existiert, vegetiert vor sich hin und wird fleissig ignoriert: http://code.google.com/p/primefaces/issues/detail?id=6187&can=1&q=validateId&colspec=ID%20Type%20Status%20Priority%20TargetVersion%20Reporter%20Owner%20Summary

Gruß,
Tim