简体   繁体   English

Struts2 - 如何在验证错误后使用提交的值重新填充具有动态生成的字段名(通过表达式)的表单

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

We have a Struts2 application where we are building survey feature using which application users will be able to create surveys by adding different questions.我们有一个 Struts2 应用程序,我们正在构建调查功能,使用该应用程序用户将能够通过添加不同的问题来创建调查。

These questions are comprised of question text and an html control for getting response.这些问题由问题文本和用于获取响应的 html 控件组成。

The html controls so far supported are single and multi-select list , text field , textarea , checkbox and radiobox .目前支持的html控件是单选多选列表文本字段文本区域复选框和单选框。

So the survey form renders displaying all questions added by the user where each question has its question text displayed followed by the html field control selected for that question.因此,调查表单呈现显示用户添加的所有问题,其中每个问题都显示其问题文本,然后是为该问题选择的 html 字段控件。

Basically, it is a dynamic form where form field names are being dynamically generated as all surveys will be different and therefore there are no properties in the Action class backing survey form fields.基本上,它是一个动态表单,其中表单字段名称是动态生成的,因为所有调查都会不同,因此在 Action class 支持调查表单字段中没有属性。

We are generating form field name using prefix question_ appended with database id of the question to represent each question response input field uniquely.我们使用前缀question_生成表单字段名称,并附加问题的数据库 id 以唯一地表示每个问题响应输入字段。 Here is a snippet from our JSP page to make things clear.这是我们 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>

On form submit, in the action class we are using HttpServletRequest to get submitted form field values.在提交表单时,在操作 class 中,我们使用HttpServletRequest来获取提交的表单字段值。 The way we identify which question the answer belongs to is through the request parameter name which as can be seen in the above JSP snippet starts with prefix 'question_' followed by question Id.我们识别答案属于哪个问题的方法是通过请求参数名称,如上面的 JSP 片段中所示,以前缀“question_”开头,后跟问题 ID。 So we split the parameter name to get the question id and associate value against that question.因此,我们拆分参数名称以获取问题 id 并将值与该问题相关联。

The problem we are facing is with repopulating survey form with submitted values when the page is presented back to the user in case of validation error as the parameter names are dynamic and cannot be backed by properties defined in Action class.我们面临的问题是,在验证错误的情况下,当页面返回给用户时,使用提交的值重新填充调查表单,因为参数名称是动态的,并且不能由 Action class 中定义的属性支持。

I have tried to populate radio button and textarea fields using the below code and several other ways but to no avail我尝试使用以下代码和其他几种方式填充单选按钮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" />

Below is the action mapping for survey submit action以下是调查提交操作的操作映射

<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>

Here is the code in Action class handling the submit logic:下面是 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");
            }
        }
    }
}

We have finally solved the problem.我们终于解决了这个问题。 The initial implementation was heading in a completely wrong direction but thanks to the clue from @RomanC, we re-factored the code and removed the direct use of HttpServletRequest to finally have a working solution.最初的实现朝着完全错误的方向前进,但感谢@RomanC 提供的线索,我们重构了代码并删除了对HttpServletRequest的直接使用,最终得到了一个可行的解决方案。 Core idea was to use a Bean for capturing response and have a List of those beans in the Action class corresponding to each survey question.核心思想是使用 Bean 来捕获响应,并在对应于每个调查问题的 Action class 中List这些 bean。 On form submit, response is captured into the bean objects behind the scene by framework itself and thus available for further logic processing in submit handler action method.在表单提交时,框架本身会将响应捕获到场景背后的 bean 对象中,因此可用于提交处理程序操作方法中的进一步逻辑处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM