2010-10-04 62 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"); 
} 

我可以這樣活,除非別人有一個簡單的替代我的做法。

相關問題