You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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关联问题与输入组件

实现步骤

  1. 在CreateSurvey类中添加全局Map,用于存储每个问题对应的输入组件:
private Map<Question, Component> questionInputMap = new HashMap<>();
  1. 修改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;
}
  1. 在提交按钮点击事件中遍历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:自定义组件封装问题与输入

实现步骤

  1. 创建自定义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());
    }
}
  1. 修改CreateSurvey类中的组件创建逻辑,使用自定义组件:
// 替换原有的for循环
for(Question question: questionService.findQuestion(survey.getId())){
    add(new QuestionResponseComponent(question));
}
  1. 提交按钮点击事件处理:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 18:40:59