2010-10-04 57 views
0

我有一个JSF应用程序,其中包含两个JSP页面,这两个JSP页面都显示来自会话作用域容器对象的一些相同数据。每个页面都以不同的方式显示数据,每个数据表在页面之间不同。到目前为止,这一切都正常。JSF - 如何从支持bean动作方法内部确定当前JSP页面

我的问题是,我一直在欺骗一点,我怎么弄清楚我的支持bean操作方法里面请求了什么页面。在每个页面上,我使用了一个绑定我的数据表。

draftReport.jsp

<t:dataTable 
    border="1" 
    id="reportDraftDataTable" 
    binding="#{controller.reportDraftDataTable}" 
    value="#{sessionData.reportDraftAdapterList}" 
    var="currentRow" 
    rowClasses="dataTableOddRow, dataTableEvenRow"> 

report.jsp

<t:dataTable 
    border="1" 
    id="reportDataTable" 
    binding="#{controller.reportDataTable}" 
    value="#{sessionData.reportAdapterList}" 
    var="currentRow" 
    rowClasses="dataTableOddRow, dataTableEvenRow"> 

我有一个请求范围的辅助bean(名为Controller)与一些对这些作用的方法页面。我不想在backing bean上重复代码(每个类似的JSP页面都有一个类似的方法),我想知道哪些页面正在被渲染,并将它用作通用处理程序方法(可以处理来自两个页面的操作)的参数支持豆。所以,我被骗了,做这样的:

public class Controller { 
    ... 

    private HtmlDataTable preArrivalReportDataTable; 
    private HtmlDataTable preArrivalReportDraftDataTable; 
    private static enum ReportType { 
     NON_DRAFT, 
     DRAFT 
    } 
    ... 

    private ReportType determineReportTypeFromControlBindings() { 
     Validate.isTrue(this.preArrivalReportDataTable != null^
       this.preArrivalReportDraftDataTable != null, 
      "Either preArrivalReportDataTable XOR " + 
      "preArrivalReportDraftDataTable must be null in " + 
      "determineReportTypeFromControlBindings()"); 
     if (this.preArrivalReportDataTable != null) { 
      return ReportType.NON_DRAFT; 
     } else { 
      return ReportType.DRAFT; 
     } 
    } 
    ... 

    public String actionOnReport() { 
     ReportType reportType = null; 
     reportType = determineReportTypeFromControlBindings(); 
     handleReportAction(reportType); 
     return "REFRESH"; 
    } 
    ... 
} 

这个工作在我的控制器类确定内部操作方法,但我需要补充一点,终于打破了我的哈克代码的另一种方法:

public String getStyleClass() { 
     ReportType reportType = determineReportTypeFromControlBindings(); 
     switch (reportType) { 
      case NON_DRAFT: 
       return "styleA"; 
      case DRAFT: 
       return "styleB"; 
      default: 
       return null; 
     } 
    } 

在我的JSP ,JSF-EL表达式位于数据表控件绑定之上,我在后台bean中使用该数据表来确定我在哪个页面上。此时determineReportTypeFromControlBindings()在Validate检查中抛出一个异常,大概是因为控制绑定还没有发生。

我并不惊讶这发生了。它总是觉得是错误的方式。但我的问题是:

从请求范围的支持bean操作方法确定当前请求的JSP页面的正确方法是什么?

如果相关,我使用MyFaces 1.2 Tomahawk标签库。

+0

您无法创建一个适当的Controller,与传递给JSF的View/Data bean分开进行呈现?一个清晰的MVC设计几乎可以肯定比黑客控制器更好的模型。 – 2013-05-13 01:13:22

回答

1

我可以想到几种方法,一种是积极主动的,其中页面告诉bean什么是视图,一个是反应式的,其中bean推断视图。最后一个将是一个具有草稿和非草稿的具体实现的抽象bean。

我更喜欢最后一种方法,它感觉最像Java一样,也是最不起作用的。但这里有一些关于如何做前两个的基本想法。

主动:在renderResponse阶段调用方法来设置报告类型。我只在会话范围的bean中完成了这项工作,不确定它在请求范围内的工作性能如何,您可能需要检查其他阶段,或者可能只是应用它而不考虑实际阶段。

Controller.java

public void draftInitializer(PhaseEvent event) { 
    if (event.getPhaseId().equals(PhaseId.RENDER_RESPONSE)) { 
    reportType = DRAFT; 
    } 
} 

draftReport。JSP

<f:view beforePhase="#{controller.draftInitializer}"> 

反应:获取从请求的URL。

Controller.java

private String getRequestURL(){ 
    HttpServletRequest request = (HttpServletRequest)FacesContext.getExternalContext().getRequest(); 
    return request.getRequestURL(); 
    } 

    private boolean isDraft() { 
    return getRequestURL().contains(DRAFT_URL_IDENTIFIER); 
    } 
+0

+1您的建议。我决定采用另一个类似于“被动”方法的方向,但使用UIViewRoot而不是请求URL。 – 2010-10-05 14:27:20

0
所以

我结束了通过检查对从FacesContext请求期间获得的UIViewRoot对象解决这个。我将ReportType enum替换为RequestedPage枚举,因为它看起来更具可读性。

public static enum RequestedPage { 
    REPORT, 
    REPORT_DRAFT, 
    UNKNOWN 
} 

然后我在我的Controller类中创建了一对字符串常量和一个新方法。

private final static String REPORT_DRAFT_JSP_NAME = "draftReport.jsp"; 
private final static String REPORT_JSP_NAME = "report.jsp"; 

/** 
* This method should only be invoked from inside an action method. 
* An exception will be thrown if the method is called when either 
* the FacesContext or UIViewRoot are not available. This is normally 
* the case outside of an active request or before the RESTORE_VIEW 
* phase has been completed. 
* 
* @return A non-null RequestedPage reference 
*/ 
private RequestedPage determineRequestedPageFromViewId() { 
    FacesContext facesContext = FacesContext.getCurrentInstance(); 
    Validate.notNull(facesContext); 
    UIViewRoot uiViewRoot = facesContext.getViewRoot(); 
    Validate.notNull(uiViewRoot); 
    String viewId = uiViewRoot.getViewId(); 
    logger.info("view id: " + viewId); 
    RequestedPage requestedPage = null; 
    if (viewId.contains(REPORT_DRAFT_JSP_NAME)) { 
     requestedPage = RequestedPage.REPORT_DRAFT; 
    } else if (viewId.contains(REPORT_JSP_NAME)) { 
     requestedPage = RequestedPage.REPORT; 
    } else { 
     requestedPage = RequestedPage.UNKNOWN; 
    } 
    return requestedPage; 
} 

UNKNOWN枚举的意思涵盖其身份,我不,我的操作方法关心的所有网页。只要我遵守Javadoc评论中提到的约束条件,这似乎可以正常工作。

这种方法唯一令人失望的地方是我想在我的Controller类的初始化方法内部执行RequestedPage分辨率。不幸的是,这不起作用,因为在RESTORE_VIEW阶段开始之前调用初始化程序,因此UIViewRoot仍为空。

这里是代码,将工作:

@PostConstruct 
public void init() { 
    logger.info("init() has been invoked"); 
    RequestedPage requestedPage = 
     determineRequestedPageFromViewId(); 
    // An exception is always thrown before I get here... 
    this.theRequestedPage = requestedPage; 
    logger.info("init() finished"); 
} 

我可以这样活,除非别人有一个简单的替代我的做法。

相关问题