如何在Vaadin Flow 23中收集动态生成表单的用户回答?
问题:Vaadin Flow 23动态问卷表单无法获取用户提交的响应
我正在用Vaadin Flow 23开发问卷收集Web应用,通过数据库数据动态构建问卷表单,循环调用createQuestionRow方法生成对应表单组件。目前表单已完成动态渲染,但在提交按钮的点击事件里,不知道怎么获取用户填写的问卷响应,求实现方案。
现有代码
public class CreateSurvey extends VerticalLayout{ TextField title = new TextField("Title"); TextField preamble = new TextField("Preamble"); TextField description = new TextField("Description"); TextField category = new TextField("Category"); SurveyService surveyService; QuestionService questionService; Button submit = new Button("Submit"); public CreateSurvey(SurveyService surveyService,QuestionService questionService) { this.surveyService = surveyService; this.questionService=questionService; Survey survey = surveyService.findSurvey(1L); title.setValue(survey.getTitle()); title.setReadOnly(true); preamble.setValue(survey.getPreamble()); preamble.setReadOnly(true); description.setValue(survey.getDescription()); description.setReadOnly(true); category.setValue(survey.getCategory().toString()); category.setReadOnly(true); FormLayout layout = new FormLayout(); layout.add(title,preamble,description,category); layout.setResponsiveSteps(new FormLayout.ResponsiveStep("0",1)); add(layout); for(Question question: questionService.findQuestion(survey.getId())){ add(createQuestionRow(question)); } getChildren().forEach(component-> { System.out.println(component.getElement().toString()); }); submit.addClickListener(buttonClickEvent -> { }); } private Component createQuestionRow(Question question) { FormLayout layout =new FormLayout(); List<String> answers = new ArrayList<>(); H5 qOrder = new H5(String.valueOf(question.getQuestionOrder())); H6 statement = new H6(question.getStatement()); HorizontalLayout statementLayout = new HorizontalLayout(); statementLayout.add(qOrder,statement); statementLayout.setAlignItems(FlexComponent.Alignment.BASELINE); RadioButtonGroup<String> optionsRadio = new RadioButtonGroup<>(); TextField shortAnswer = new TextField(); List<String> list; Checkbox required = new Checkbox("Is Required"); required.setValue(question.isRequired()); required.isReadOnly(); VerticalLayout options = new VerticalLayout(); if(question.getOption()== Option.linearTen){ optionsRadio.setItems("10","9","8","7","6","5","4","3","2","1"); options.add(optionsRadio); } else if (question.getOption()== Option.AnnotatedFour) { optionsRadio.setItems("Excellent","Very good","good","bad"); options.add(optionsRadio); } else if ( question.getOption()==Option.shortAnswer){ options.add(shortAnswer); } optionsRadio.addValueChangeListener(click-> { answers.add(click.getValue()); System.out.println(click.getValue()); }); layout.add(statementLayout,options,required); layout.setResponsiveSteps(new FormLayout.ResponsiveStep("0",2)); layout.setColspan(statementLayout,2); return layout; }
问卷单选按钮组效果

解决方案
下面提供两种可行的实现方案,可根据项目需求选择:
方案1:用Map关联问题与输入组件
实现步骤
- 在
CreateSurvey类中添加全局Map,用于存储每个问题对应的输入组件:
private Map<Question, Component> questionInputMap = new HashMap<>();
- 修改
createQuestionRow方法,将当前问题和对应的输入组件存入Map:
private Component createQuestionRow(Question question) { FormLayout layout = new FormLayout(); H5 qOrder = new H5(String.valueOf(question.getQuestionOrder())); H6 statement = new H6(question.getStatement()); HorizontalLayout statementLayout = new HorizontalLayout(qOrder, statement); statementLayout.setAlignItems(FlexComponent.Alignment.BASELINE); RadioButtonGroup<String> optionsRadio = new RadioButtonGroup<>(); TextField shortAnswer = new TextField(); Checkbox required = new Checkbox("Is Required"); required.setValue(question.isRequired()); required.setReadOnly(true); VerticalLayout options = new VerticalLayout(); // 记录当前问题对应的输入组件 Component inputComponent; if(question.getOption()== Option.linearTen){ optionsRadio.setItems("10","9","8","7","6","5","4","3","2","1"); options.add(optionsRadio); inputComponent = optionsRadio; } else if (question.getOption()== Option.AnnotatedFour) { optionsRadio.setItems("Excellent","Very good","good","bad"); options.add(optionsRadio); inputComponent = optionsRadio; } else if ( question.getOption()==Option.shortAnswer){ options.add(shortAnswer); inputComponent = shortAnswer; } else { // 处理未定义的问题类型,默认空组件 inputComponent = new Div(); } // 将问题与输入组件绑定存入Map questionInputMap.put(question, inputComponent); layout.add(statementLayout, options, required); layout.setResponsiveSteps(new FormLayout.ResponsiveStep("0",2)); layout.setColspan(statementLayout,2); return layout; }
- 在提交按钮点击事件中遍历Map,获取用户响应并处理:
submit.addClickListener(buttonClickEvent -> { Map<Question, String> surveyResponses = new HashMap<>(); boolean hasValidationError = false; for (Map.Entry<Question, Component> entry : questionInputMap.entrySet()) { Question question = entry.getKey(); Component input = entry.getValue(); String response = null; // 根据组件类型获取用户输入 if (input instanceof RadioButtonGroup<?>) { response = ((RadioButtonGroup<String>) input).getValue(); } else if (input instanceof TextField) { response = ((TextField) input).getValue(); } // 必填项校验 if (question.isRequired() && (response == null || response.trim().isEmpty())) { Notification.show("请填写必填问题:" + question.getStatement()); hasValidationError = true; break; } surveyResponses.put(question, response); } if (!hasValidationError) { // 调用业务层保存响应数据 surveyService.saveSurveyResponses(surveyResponses); Notification.show("问卷提交成功"); } });
方案2:自定义组件封装问题与输入
实现步骤
- 创建自定义
QuestionResponseComponent,封装问题和对应的输入组件:
public class QuestionResponseComponent extends FormLayout { private final Question question; private final Component inputComponent; public QuestionResponseComponent(Question question) { this.question = question; // 初始化问题标题布局 H5 qOrder = new H5(String.valueOf(question.getQuestionOrder())); H6 statement = new H6(question.getStatement()); HorizontalLayout statementLayout = new HorizontalLayout(qOrder, statement); statementLayout.setAlignItems(FlexComponent.Alignment.BASELINE); // 创建对应类型的输入组件 if(question.getOption()== Option.linearTen){ RadioButtonGroup<String> optionsRadio = new RadioButtonGroup<>(); optionsRadio.setItems("10","9","8","7","6","5","4","3","2","1"); inputComponent = optionsRadio; } else if (question.getOption()== Option.AnnotatedFour) { RadioButtonGroup<String> optionsRadio = new RadioButtonGroup<>(); optionsRadio.setItems("Excellent","Very good","good","bad"); inputComponent = optionsRadio; } else if ( question.getOption()==Option.shortAnswer){ inputComponent = new TextField(); } else { inputComponent = new Div(); } // 添加必填项标识 Checkbox required = new Checkbox("Is Required"); required.setValue(question.isRequired()); required.setReadOnly(true); // 组装布局 VerticalLayout optionsLayout = new VerticalLayout(inputComponent); add(statementLayout, optionsLayout, required); setResponsiveSteps(new FormLayout.ResponsiveStep("0", 2)); setColspan(statementLayout, 2); } // 获取当前问题 public Question getQuestion() { return question; } // 获取用户输入的响应 public String getResponse() { if (inputComponent instanceof RadioButtonGroup<?>) { return ((RadioButtonGroup<String>) inputComponent).getValue(); } else if (inputComponent instanceof TextField) { return ((TextField) inputComponent).getValue(); } return null; } // 校验必填项是否为空 public boolean isRequiredEmpty() { return question.isRequired() && (getResponse() == null || getResponse().trim().isEmpty()); } }
- 修改
CreateSurvey类中的组件创建逻辑,使用自定义组件:
// 替换原有的for循环 for(Question question: questionService.findQuestion(survey.getId())){ add(new QuestionResponseComponent(question)); }
- 提交按钮点击事件处理:
submit.addClickListener(buttonClickEvent -> { Map<Question, String> surveyResponses = new HashMap<>(); boolean hasError = false; // 遍历所有组件,筛选出自定义的QuestionResponseComponent for (Component component : getChildren().toList()) { if (component instanceof QuestionResponseComponent) { QuestionResponseComponent qrc = (QuestionResponseComponent) component; if (qrc.isRequiredEmpty()) { Notification.show("请填写必填问题:" + qrc.getQuestion().getStatement()); hasError = true; break; } surveyResponses.put(qrc.getQuestion(), qrc.getResponse()); } } if (!hasError) { surveyService.saveSurveyResponses(surveyResponses); Notification.show("问卷提交成功"); } });
内容的提问来源于stack exchange,提问作者akram seid
相关产品推荐
相关产品推荐

