使用PrimeFaces 3.5在JSF2.0中自定义ManagedBean的范围

时间:2013-04-04 22:51:35

标签: jsf-2 primefaces

以下是我的要求 - enter image description here

我有一个p:panelGrid,可以添加&删除表格行。网格包含一些p:inputText和各种其他PrimeFaces组件以及每行中的p:fileUpload组件。组件p:fileUpload设置了mode="advanced" auto="true"个属性,自动上传文件并在完成成功上传后自行隐藏。

整个p:panelGrid位于@ViewScoped,因此工作正常。我在p:fileUpload中保留了@RequestScoped个组件,因为对于每个上传请求,它必须上传文件,但在添加新行之后,之前的状态不再保留。所以p:fileUpload也开始在之前的行中可见。这就是我不想要的。我需要为它编写任何自定义范围吗? 以下是视图 - |

<h:form>
    <p:panel id="agentForm" header="#{msg.AGENTS_INFORMATION}"
        style="overflow:auto;  margin-bottom: 2px">
        <div align="center" style="margin-top: 20px; margin-bottom: 2px">
            <ui:repeat value="#{agent.scenarioList}" var="c">
                <p:panelGrid>
                    <p:row>
                        <p:column>
                            <p:inputText id="ipaddress" value="#{c.machineIpAddress}"
                                style="width:90%">
                                <p:watermark for="ipaddress" value="#{msg.MACHINE_IP_ADDRESS}" />
                            </p:inputText>
                        </p:column>
                        <p:column>
                            <p:inputText id="username" value="#{c.machineUsername}"
                                style="width:90%">
                                <p:watermark for="username" value="#{msg.MACHINE_USERNAME}" />
                            </p:inputText>
                        </p:column>
                        <p:column>
                            <p:password id="passwd" value="#{c.machinePassword}">
                                <p:watermark for="passwd" value="#{msg.MACHINE_PASSWORD}" />
                            </p:password>
                        </p:column>
                        <p:column id="fileUpload">
                            <p:fileUpload rendered="#{!fileUploadController.hidden}"
                                label="Upload Script" style="font-size: 100% !important;"
                                showButtons="false"
                                fileUploadListener="#{fileUploadController.upload}"
                                mode="advanced" auto="true" sizeLimit="100000"
                                allowTypes="/(\.|\/)(py|txt)$/"
                                update="fileUpload, outPanel, :message" />
                            <p:outputPanel id="outPanel">
                            <!-- Below outputLabel will be linked to uploaded file, so that User can see the file -->
                            <p:outputLabel style="cursor: pointer" value="View uploded Script"
                                    label="View Script" rendered="#{fileUploadController.hidden}" />
                            </p:outputPanel>
                        </p:column>
                        <p:column>
                            <p:inputText id="testname" value="#{c.testName}"
                                style="width:90%">
                                <p:watermark for="testname" value="#{msg.TEST_NAME}" />
                            </p:inputText>
                        </p:column>
                        <p:column>
                            <p:spinner id="threads" value="#{c.threads}" min="1" max="500"
                                size="8">
                                <p:tooltip for="threads" value="#{msg.TEST_NAME}"
                                    showEffect="slide" hideEffect="slide" />
                            </p:spinner>
                        </p:column>
                        <p:column>
                            <p:selectBooleanCheckbox id="chkSelected" value="#{c.selected}">
                                <p:tooltip for="chkSelected" value="#{msg.CHECKBOX}"
                                    showEffect="slide" hideEffect="slide" />
                            </p:selectBooleanCheckbox>
                        </p:column>
                    </p:row>
                </p:panelGrid>
            </ui:repeat>
            <p:toolbar style="margin-top: 10px;">
                <p:toolbarGroup align="right">
                    <p:commandButton value="#{msg.ADD_IT}"
                        update=":message, agentForm"
                        actionListener="#{agent.addComponent()}" />
                    <p:commandButton value="#{msg.DELETE_IT}"
                        update=":message, agentForm"
                        actionListener="#{agent.deleteComponent()}" />
                </p:toolbarGroup>
            </p:toolbar>
        </div>
    </p:panel>
</h:form>

@ViewScoped中的托管bean看起来像这样 -

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import org.ravij.performance.model.Scenario;
@ManagedBean(name = "agent")
@ViewScoped
public class AgentInfo implements Serializable {
    private static final long serialVersionUID = 1L;
    List<Scenario> scenarioList;
    @PostConstruct
    public void initBean() {
        this.scenarioList = new ArrayList<Scenario>();
        this.scenarioList.add(new Scenario());
    }
    public void addComponent() {
        if (this.scenarioList != null) {
            this.scenarioList.add(new Scenario());
        } else {
            this.initBean();
        }
    }
    public void deleteComponent() {
        List<Scenario> itemsToDelete = new ArrayList<Scenario>();
        if (this.scenarioList != null) {
            for (Scenario b : this.scenarioList) {
                if (b.isSelected()) {
                    itemsToDelete.add(b);
                }
            }
            this.scenarioList.removeAll(itemsToDelete);
        }
    }
    public List<Scenario> getScenarioList() {
        return scenarioList;
    }
    public void setScenarioList(List<Scenario> scenarioList) {
        this.scenarioList = scenarioList;
    }
}

Scenario对象包含行的所有信息。以下是代码 -

package org.ravij.performance.model;
import java.io.Serializable;
public class Scenario implements Serializable {
    private String machineIpAddress;
    private String machineUsername;
    private String machinePassword;
    private String uploadedFilePath;
    private String testName;
    private int threads = 1;
    private boolean selected = false;

    //Below are the getters and setter w.r.t all the above variables
    //I am not putting it, to make the code short
}

托管bean FileUploadController位于@RequestScoped

1 个答案:

答案 0 :(得分:1)

您应该将hidden属性与@ViewScoped bean中的其他值保持一致。您当前的代码只有一个hidden属性与您的所有<p:fileUpload组件共享,这可能不是您想要的。

行为看起来不错,因为您只是更新了当前的fileUpload,但根据您的代码,所有其他<p:fileUpload组件都应该被隐藏。

您还应该将<h:form放入<ui:repeat,这样您就可以通过放置索引之类的内容来了解​​正在上传的文件所涉及的当前行(您可以从{ {1}}使用<ui:repeat属性)或任何其他标识符来匹配隐藏输入中的当前行。

varStatus获取隐藏参数的最简单方法是从#{fileUploadController.upload}获取响应,如下所述:How to get parametrs to BackingBean from jsf page in <ui:repeat>

<强>更新

这比预期的要困难一点,问题是FacesContext会以封闭的形式发送所有内容(不尝试使用<p:fileUpload属性),因此很难知道文件上传涉及哪一行。

此外,我不知道您无法将process放入<h:form,但删除按钮的行为是阻止的,因为它希望以一种形式获取所有内容。

我使用对话框创建了一个工作POC,将文件上载放在外面,方法如下:

琐碎的Scenario.java:

<ui:repeat

AgentInfo.java中的一些更改:

public class Scenario implements Serializable {

    private String machineIpAddress;
    private String machineUsername;
    private String machinePassword;
    private String uploadedFilePath;
    private String testName;
    private int threads = 1;
    private boolean selected = false;
    private boolean hidden = false; // This is new

    // + Getters/Setters
}

大多数更改都在视图中,我放了一个@ManagedBean(name = "agent") @ViewScoped public class AgentInfo implements Serializable { private List<Scenario> scenarioList; private Scenario currentScenario; // This is new // I removed the @PostConstruct which I rarely use public void addComponent() { if (this.scenarioList != null) { this.scenarioList.add(new Scenario()); } } public void deleteComponent() { if (this.scenarioList == null) { return; } List<Scenario> itemsToDelete = new ArrayList<Scenario>(); for (Scenario scenario : this.scenarioList) { if (scenario.isSelected()) { itemsToDelete.add(scenario); } } this.scenarioList.removeAll(itemsToDelete); } // This is new, it must be called before opening the upload dialog // in order to keep a pointer on the current scenario you are working on public void prepareUpload(Scenario scenario) { this.currentScenario = scenario; } // I put the upload method here public void upload(FileUploadEvent event) { // Do what you need to do here this.currentScenario.setHidden(true); RequestContext.getCurrentInstance().execute("uploadDialogWidget.hide()"); } public List<Scenario> getScenarioList() { if (this.scenarioList == null) { this.scenarioList = new ArrayList<Scenario>(); this.scenarioList.add(new Scenario()); } return scenarioList; } public void setScenarioList(List<Scenario> scenarioList) { this.scenarioList = scenarioList; } public Scenario getCurrentScenario() { return currentScenario; } public void setCurrentScenario(Scenario currentScenario) { this.currentScenario = currentScenario; } } 来打开表单中的对话框。我还添加了对话框,并为您的密码字段添加了<h:commandButton属性(如果您希望在表单提交后保留值,则必须具有此属性)。 请注意,我删除了对redisplay id的组件的引用,该组件未提供,不要忘记重新引入它。 .xhtml:

message

您应该考虑从<h:form id="agentForm"> <p:panel header="#{msg.AGENTS_INFORMATION}" style="overflow:auto; margin-bottom: 2px"> <div align="center" style="margin-top: 20px; margin-bottom: 2px"> <ui:repeat value="#{agent.scenarioList}" var="c"> <p:panelGrid> <p:row> <p:column> <p:inputText id="ipaddress" value="#{c.machineIpAddress}" style="width:90%"> <p:watermark for="ipaddress" value="#{msg.MACHINE_IP_ADDRESS}" /> </p:inputText> </p:column> <p:column> <p:inputText id="username" value="#{c.machineUsername}" style="width:90%"> <p:watermark for="username" value="#{msg.MACHINE_USERNAME}" /> </p:inputText> </p:column> <p:column> <p:password id="passwd" value="#{c.machinePassword}" redisplay="true"> <p:watermark for="passwd" value="#{msg.MACHINE_PASSWORD}" /> </p:password> </p:column> <p:column id="fileUpload"> <p:commandButton icon="ui-icon-arrowthick-1-n" value="Upload" actionListener="#{agent.prepareUpload(c)}" update=":uploadDialog" oncomplete="uploadDialogWidget.show()" rendered="#{!c.hidden}" /> <p:outputPanel id="outPanel"> <!-- Below outputLabel will be linked to uploaded file, so that User can see the file --> <p:outputLabel style="cursor: pointer" value="View uploded Script" rendered="#{c.hidden}" /> </p:outputPanel> </p:column> <p:column> <p:inputText id="testname" value="#{c.testName}" style="width:90%"> <p:watermark for="testname" value="#{msg.TEST_NAME}" /> </p:inputText> </p:column> <p:column> <p:spinner id="threads" value="#{c.threads}" min="1" max="500" size="8"> <p:tooltip for="threads" value="#{msg.TEST_NAME}" showEffect="slide" hideEffect="slide" /> </p:spinner> </p:column> <p:column> <p:selectBooleanCheckbox id="chkSelected" value="#{c.selected}"> <p:tooltip for="chkSelected" value="#{msg.CHECKBOX}" showEffect="slide" hideEffect="slide" /> </p:selectBooleanCheckbox> </p:column> </p:row> </p:panelGrid> </ui:repeat> <p:toolbar style="margin-top: 10px;"> <p:toolbarGroup align="right"> <p:commandButton value="#{msg.ADD_IT}" update="agentForm" actionListener="#{agent.addComponent()}" /> <p:commandButton value="#{msg.DELETE_IT}" update="agentForm" actionListener="#{agent.deleteComponent()}" /> </p:toolbarGroup> </p:toolbar> </div> </p:panel> </h:form> <p:dialog id="uploadDialog" widgetVar="uploadDialogWidget" header="File upload"> <h:form rendered="#{!empty agent.currentScenario}"> <p:fileUpload label="Upload Script" style="font-size: 100% !important;" showButtons="false" fileUploadListener="#{agent.upload}" mode="advanced" auto="true" sizeLimit="100000" allowTypes="/(\.|\/)(py|txt)$/" update=":agentForm"> </p:fileUpload> <p:commandButton value="Cancel" onclick="uploadDialogWidget.hide();" onstart="return false;" /> </h:form> </p:dialog> 转移到具有内置机制的<p:panelGrid来处理行选择。