[英]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.