簡體   English   中英

Struts2 - 如何在驗證錯誤后使用提交的值重新填充具有動態生成的字段名(通過表達式)的表單

[英]Struts2 - How to repopulate form having dynamically generated fieldnames (via expression) with submitted values after validation error

我們有一個 Struts2 應用程序,我們正在構建調查功能,使用該應用程序用戶將能夠通過添加不同的問題來創建調查。

這些問題由問題文本和用於獲取響應的 html 控件組成。

目前支持的html控件是單選多選列表文本字段文本區域復選框和單選框。

因此,調查表單呈現顯示用戶添加的所有問題,其中每個問題都顯示其問題文本,然后是為該問題選擇的 html 字段控件。

基本上,它是一個動態表單,其中表單字段名稱是動態生成的,因為所有調查都會不同,因此在 Action class 支持調查表單字段中沒有屬性。

我們使用前綴question_生成表單字段名稱,並附加問題的數據庫 id 以唯一地表示每個問題響應輸入字段。 這是我們 JSP 頁面的一個片段,以說明問題。

    <s:form id="surveyForm" action="survey/submitFeedback">

      <s:iterator value="surveyQuestions">
        <p class="form-group <s:if test="%{fieldErrors.get('question_' + surveyQuestionId).size() > 0}">has-error</s:if>" >
          <label class="control-label" >
            <s:property value="questionText"/>
          </label>
          <s:if test="required" ><span style='color:red'>*</span></s:if>
          <br>
          <s:if test="surveyQuestionType.type == @com.reach150.enumeration.SurveyQuestionTypeEnum@OPENENDED_TEXTFIELD" >
            <s:textfield name="question_%{surveyQuestionId}" cssClass="form-control" maxlength="charactersLimit" />
          </s:if>
          <s:elseif test="surveyQuestionType.type == @com.reach150.enumeration.SurveyQuestionTypeEnum@OPENENDED_TEXTAREA" >
            <s:textarea name="question_%{surveyQuestionId}" style="height: 150px; width: 400px;" cssClass="form-control" maxlength="charactersLimit" />
          </s:elseif>
          <s:elseif test="surveyQuestionType.type == @com.reach150.enumeration.SurveyQuestionTypeEnum@SINGLESELECTDROPDOWN || surveyQuestionType.type == @com.reach150.enumeration.SurveyQuestionTypeEnum@MULTISELECTDROPDOWN" >
            <s:select name="question_%{surveyQuestionId}" list="orderedSelectOptions" listKey="optionValue" listValue="optionLabel" emptyOption="true" multiple="true" cssClass="form-control" />
          </s:elseif>
          <s:else>
            <s:radio name="question_%{surveyQuestionId}" list="#{'true':'Yes','false':'No'}" cssClass="radioMarginRight" />
          </s:else>
          <span class="help-block" for="question_${surveyQuestionId}">
            <s:fielderror cssClass="font-bold text-danger">
              <s:param>question_<s:property value="surveyQuestionId" /></s:param>
            </s:fielderror>
          </span>
          <br/>
        </p>      
      </s:iterator>      
      <button type="submit" class="btn btn-primary btn-lg pull-right "><s:if test="survey.requestReferral == true">Next</s:if><s:else>Done</s:else></button>
      
    </s:form>

在提交表單時,在操作 class 中,我們使用HttpServletRequest來獲取提交的表單字段值。 我們識別答案屬於哪個問題的方法是通過請求參數名稱,如上面的 JSP 片段中所示,以前綴“question_”開頭,后跟問題 ID。 因此,我們拆分參數名稱以獲取問題 id 並將值與該問題相關聯。

我們面臨的問題是,在驗證錯誤的情況下,當頁面返回給用戶時,使用提交的值重新填充調查表單,因為參數名稱是動態的,並且不能由 Action class 中定義的屬性支持。

我嘗試使用以下代碼和其他幾種方式填充單選按鈕textarea字段,但無濟於事

    <s:textarea name="question_%{surveyQuestionId}" style="height: 150px; width: 400px;" cssClass="form-control" maxlength="charactersLimit" value="#parameters.%{'question_' + surveyQuestionId}" />

    <s:radio name="question_%{surveyQuestionId}" value="#parameters.%{'question_' + surveyQuestionId}" list="#{'true':'Yes','false':'No'}" cssClass="radioMarginRight" />

以下是調查提交操作的操作映射

<action name="survey/submitFeedback" class="surveyAction" method="submitFeedback">
    <result name="success" type="tiles">survey.submit</result>
    <result name="error" type="tiles">survey.view</result>
    <param name="public">true</param>
</action>

下面是 Action class 中處理提交邏輯的代碼:

private Integer npsScore = 0;
private Map<String, String[]> surveyResponseQuestionAnswerMap = new HashMap<>();

    public String submitFeedback() {

    try {
        if (requestId == null) {
            addActionError("Request Id missing! Invalid Request!");
            throw new Exception("Invalid Request!");
        }
        
        surveyRequest = surveyService.getSurveyRequestByUUID(requestId);
        if (surveyRequest == null) {
            addActionError("Request Id Invalid! Invalid Request!");
            throw new Exception("Request Id Invalid! Invalid Request!");
        }
        loadQuestionAnswersMap();
        validateSurveyFeedback();
        
        if (hasErrors()) {
            throw new Exception("Error submitting response!");
        } else {
            surveyService.parseAndSaveSurveyResponse(surveyRequest, surveyResponseQuestionAnswerMap);
            setSurveyCustomMessages(surveyService.getSurveyCustomMessagesSettingBySurveyId(survey.getSurveyId()));
        }
        return SUCCESS;
    } catch (Exception e) {
        addActionError("Error submitting response!");
        logger.error(e);
        loadSurvey();
        return ERROR;
    }
}

private void loadQuestionAnswersMap() {
    HttpServletRequest httpRequest = ActionUtil.getRequest();
    Enumeration<String> parameterNames = httpRequest.getParameterNames();
    while (parameterNames.hasMoreElements()) {
        String parameterName = parameterNames.nextElement();
        if (parameterName.startsWith("question_")) {
            String[] values = httpRequest.getParameterValues(parameterName);
            if (values != null) {
                surveyResponseQuestionAnswerMap.put(parameterName, values);
            }
        }
    }
}

private void validateSurveyFeedback() throws Exception {
    
    HttpServletRequest httpRequest = ActionUtil.getRequest();
    Survey survey = surveyRequest.getSurvey();
    if (survey.isUseNetPromotorScore()) {
        String npsScoreStr = httpRequest.getParameter("npsScore");
        if (StringUtils.isBlank(npsScoreStr)) {
            this.addFieldError("npsScore", "Answer is required");
        } else {
            setNpsScore(Integer.valueOf(npsScoreStr));
        }
    }
    List<SurveyQuestion> requiredQuestions = surveyQuestionService.getRequiredSurveyQuestionsForSurvey(surveyRequest.getSurvey());
    for (SurveyQuestion requiredQuestion : requiredQuestions) {
        Integer requiredQuestionId = requiredQuestion.getSurveyQuestionId();
        String requiredQuestionFieldParameterName = "question_" + requiredQuestionId;
        logger.info("Required Question Field Parameter Name: " + requiredQuestionFieldParameterName);
        String[] answers = httpRequest.getParameterValues(requiredQuestionFieldParameterName);
        if (answers == null) {
            this.addFieldError(requiredQuestionFieldParameterName, "Answer is required");
        } else {
            boolean noValue = true;
            for (String answer : answers) {
                if (StringUtils.isNotBlank(answer)) {
                    noValue = false;
                    break;
                }
            }
            if (noValue) {
                this.addFieldError(requiredQuestionFieldParameterName, "Answer is required");
            }
        }
    }
}

我們終於解決了這個問題。 最初的實現朝着完全錯誤的方向前進,但感謝@RomanC 提供的線索,我們重構了代碼並刪除了對HttpServletRequest的直接使用,最終得到了一個可行的解決方案。 核心思想是使用 Bean 來捕獲響應,並在對應於每個調查問題的 Action class 中List這些 bean。 在表單提交時,框架本身會將響應捕獲到場景背后的 bean 對象中,因此可用於提交處理程序操作方法中的進一步邏輯處理。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM