0

I´m trying to create an updateable GUI using JSF 2.2 and PrimeFaces 4.0. My problem is that the model is updated but not the view.

Here is my example page. It has just a Button which should make the two Panels change their places:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:p="http://primefaces.org/ui">
    <h:head/>
    <h:body>
        <h:form>
            <p:panelGrid id="grid">
                <p:row>
                    <p:column id="slot1" style="width: 100px; height: 100px">
                        <p:panel id="panel1" binding="#{test.panel1}" style="width: 100px; height: 100px"/>
                    </p:column>
                    <p:column id="slot2" style="width: 100px; height: 100px">
                        <p:panel id="panel2" binding="#{test.panel2}" style="width: 100px; height: 100px"/>
                    </p:column>
                </p:row>
            </p:panelGrid>

            <p:commandButton id="button2" value="Switch Panels">
                <p:ajax listener="#{test.onSwitchPanels(event)}" update="grid"/>
            </p:commandButton>
        </h:form>
    </h:body>
</html>

And the Java class:

@ManagedBean
@SessionScoped
public class Test {

Panel panel1 = new Panel();
Panel panel2 = new Panel();

public Test() {
    panel1.setHeader("Panel 1");
    panel2.setHeader("Panel 2");
}

public Panel getPanel1() {
    return panel1;
}

public void setPanel1(Panel panel) {
    this.panel1 = panel;
}

public Panel getPanel2() {
    return panel2;
}

public void setPanel2(Panel panel) {
    this.panel2 = panel;
}

public void onSwitchPanels(ActionEvent event) {
    Panel tmp = panel1;
    panel1 = panel2;
    panel2 = tmp;
}
}

Debugging shows me that the getters and setters are called after onSwitchPanels() with the correct new values when the button is clicked. But as I wrote the view is not updated. This happens only after a manual page refresh or the next click (which of course sets the model back to it´s original state but shows the switched panels). I already tried update="@form" and update="slot1 slot2" without luck...

0

4 Answers 4

2

This code is honestly a WTF. Binding UI component instances to a session scoped bean is recipe for trouble. UI component instances are namely request scoped.

Even if you used a request scoped bean, this wouldn't work without rebuilding the view. The UI component tree is created during building of the view. You're manipulating the component references after building the view and expecting that the changes are magically reflected in the UI component tree. This is not true. Only their attributes would be reflected (thus, when you do e.g. panel1.setHeader(panel2.getHeader()) and vice versa, then it'll "work".

It isn't entirely clear why you were thinking in this direction. Generally you should manipulate the model dynamically, not the view (the UI components do not represent the model, they represent the view; making them properties of the model/controller doesn't magically make it the model). One of most straightforward ways is having a list of entities and present them in a dynamic <p:dataGrid> instead of a hardcoded <p:panelGrid> whereby you dynamically set the desired component attribtues based on the currently iterated model item.

E.g. this

<h:form>
    <p:dataGrid value="#{bean.items}" var="item" columns="2">
        <p:column><p:panel header="#{item.header}" /></p:column>
    </p:dataGrid>
    <p:commandButton value="swap" action="#{bean.swap}" update="grid" />    
</h:form>

with this in a @ViewScoped(!) bean:

private List<Item> items; // +getter

@PostConstruct
public void init() {
    items = new ArrayList<>();
    items.add(new Item("Panel 1"));
    items.add(new Item("Panel 2"));
}

public void swap() {
    items.add(items.remove(0)); // Note: works only if you've only 2 items ;)
}

The Item class is just a javabean with a header property. The average IDE can autogenerate the whole class.

See also:

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot. I was just prototyping. My problem is, that it is not enough for the desired functionality to change some attributes. I want to be able to have e.g. a Panel with a chart, a Panel with some text and some self written components, that can be rearranged freely in a grid. If this can only be done by reloading the view, then so be it. But perhaps JSF is not the right coice for my use case anyway...
If you want to conditionally build the view instead of conditionally render the view, then grab JSTL.
0

I'm a bit rusty on my JSF / PF, but have you tried:

update="panel1 panel2"

Another thing to try is using

actionListener"#{test.onSwitchPanels}"

instead of

listener="#{test.onSwitchPanels(event)}"

10 Comments

I tried the first but it didn´t work. The second is not possible in p:ajax.
As far as I know, in JSF2.2 / Primefaces 4 you no longer need to specify p:ajax manually, all ajax-enabled components can update other components directly. Take a look here: primefaces.org/showcase-labs/ui/pprCounter.jsf
Ah, ok. So I tried <p:commandButton id="button2" value="Switch Panels" actionListener="#{test.onSwitchPanels}" update="grid"/> but the result is the same. (For attribute update I also tried @form, slot1 slot2 and panel1 panel2.)
Ahhh I remember now having a similar problem a while back. It's to do with the lifecycle where 'form' is involved (essentially the form submits itself or something and refreshes the page incorrectly). I don't have my code with me to see exactly how I fixed it, but there are three things you can try off the top of my head: 1. Add "id='myform'" to the form tag and get your update to update 'myform', 2. create a new form, and have 'grid' and 'button' in different forms, 3. wrap your form in a panel and update the whole panel. If none of these work, I will dig out my old code this evening!
Thanks a lot! I tried all three tips but none worked :( It would be great if you could find your solution...
|
0

Why not removing the 'event' parameter from the onSwitchPanels() method? you are not using it any way. You could fire it by

<p:commandButton id="button2" value="Switch Panels" action="#{test.onSwitchPanels}" update="grid" />

1 Comment

just wondering, since my answer was the selected one for 1-2 days : Did my solution work for you ?
0

I found a solution adopting this example: http://www.wobblycogs.co.uk/index.php/computing/jee/70-dynamic-dashboard-with-primefaces-part-2

The main point is to create the components programmatically. In the JSF page I just write something like <p:panelGrid id="grid" binding="#{gridBacker.grid}"/> and use the bean to create the rows, columns and panels using Application.createComponent(). The Panels are than added again after the event.

So of course BalusC was right that the action has to take place in the model.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.