2

I looking for help in understanding how to create a new object through a form which has the user select multiple sub-objects (which will come pre-populated) and available to select with a checkbox.

OrderController.java

@RequestMapping(value = { "/order" }, method = RequestMethod.GET)
public String order(ModelMap model) {

    List<Exam> exams = examService.findAllExams();
    List<Document> documents = documentService.findAllDocuments();

    model.addAttribute("exams", exams);
    model.addAttribute("documents", documents);

    return "order"; // jsp page reference
}

Order.java

@Entity
@Table(name="\"order\"")
    public class Order implements Serializable
    {
        private static final long serialVersionUID = 1L;

        @Id 
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name = "order_id", unique = true, nullable = false)
        private Integer id;

        @GeneratedValue(generator = "uuid")
        @GenericGenerator(name = "uuid", strategy = "uuid2")
        @Column(name = "uuid", unique = true, nullable = false)
        private String uuid;

        @Temporal(TemporalType.DATE)
        @Column(name = "order_date", unique = true, nullable = false)
        private Date orderDate;

        @Column(name="order_status", nullable=false)
        private String orderStatus;

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "user_id", nullable = false)
        private User user;

        @OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
        private Set<OrderExam> orderExams = new HashSet<OrderExam>(0);

        @OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
        private Set<OrderDocument> orderDocuments = new HashSet<OrderDocument(0); 

        //getters & setters
    }

OrderExam.java

@Entity
@Table(name="order_exam")
public class OrderExam implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "order_exam_id", unique = true, nullable = false)
    private Integer id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id", nullable = false)
    private Order order;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "exam_id", nullable = false)
    private Exam exam;

    @Column(name="exam_amount", nullable=true)
    private Integer examAmount;

    @Column(name="answer_sheet_amount", nullable=true)
    private String answerSheetName;

    @Column(name="students_per_csv", nullable=true)
    private String studentsPerCSV;

    @Column(name="pas", nullable=true)
    private Boolean pearsonAnswerSheet;

    //getters & setters
}

Exam.java

@Entity
@Table(name="exam")
public class Exam implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "exam_id", unique = true, nullable = false)
    private Integer id;

    @NotEmpty
    @Column(name="name", unique=true, nullable=false)
    private String name;

    @NotEmpty
    @Column(name="code", unique=true, nullable=false)
    private String code;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "exam")
    private Set<OrderExam> exams = new HashSet<OrderExam>(0);

     //getters & setters
}

As you can see I am passing in a list of exams and documents which will populate a form with available options (can be seen in the image below (exams anyway)). The user needs to be able to select multiple rows, so that a single order has multiple exams and documents associated to it.

OrderForm:Exams

My order.jsp is a little much to post the entire thing here but here is the part I have which is displayed in the image above.

Order.jsp

<form:form method="POST" modelAttribute="order" class="form-horizontal form-label-left">
<c:forEach items="${exams}" var="exam">
                                                <tr>
                                                    <th scope="row"><input type="checkbox" class="flat"></th>
                                                    <td><input id="middle-name" type="text" name="middle-name" readonly="readonly" value="${exam.name} - ${exam.code}" class="form-control col-md-7 col-xs-12"></td>
                                                    <td><input id="middle-name" type="text" name="middle-name" value="0" class="form-control col-md-3 col-xs-12"></td>
                                                    <td><input id="middle-name" type="text" name="middle-name" value="0" class="form-control col-md-3 col-xs-12"></td>
                                                    <td><input id="middle-name" type="text" name="middle-name" value="0" class="form-control col-md-3 col-xs-12"></td>
                                                    <c:choose>
                                                        <c:when test="${exam.name == 'Algebra 2 (Common Core)'}">
                                                            <th scope="row"><input type="checkbox" class="flat"></th>
                                                        </c:when>
                                                        <c:otherwise>
                                                            <th scope="row"></th>
                                                        </c:otherwise>
                                                    </c:choose>
                                                </tr>
                                            </c:forEach>
<!-- Other Stuff Goes Here -->
</form:form>

So in short, would someone be willing to show me how to set up the form in the way I described above? Thanks in advance.

1 Answer 1

1

Your question is a bit broad however you could try this as below. I have only covered exams. Principal for documents will be the same.

You will need a couple of new classes to capture the submitted form inputs:

Order Form to Capture Selections

public class OrderForm{

    private List<ExamWrapper> allAvailableExams = new ArrayList<>();

    private XOptionPrintWrapper selectedWrapper;

    public OrderForm(){

    }

    //getters and setters
}

Exam Wrapper : Decorates an Exam with a 'selected' property

public class ExamWrapper{

    private boolean selected;
    private Exam exam;

    public ExamWrapper(Exam exam){
        this.exam = exams;
    }

    //getters and setters
}

Change Contoller to

public class OrderController{

    //Exams model populated by the method below
    //moved as we also need it populated on POST
    @RequestMapping(value = { "/order" }, method = RequestMethod.GET)
    public String order(ModelMap modelMap) {

        //only needed on GET so put in model here
        List<XOptionPrintWrapper> availableWrappers = //someList;
        modelMap.put("availableWrappers", availableWrappers);

        return "order"; 
    }

    //handles for submit
    //model atribute is automatically populated by the framework
    @RequestMapping(value = { "/order" }, method = RequestMethod.POST)
    public String order(@ModelAttribute("orderForm") OrderForm orderForm) {

        //process selected exams

        return "nextView"; 
    }

    //on get populates the initial model for display
    //on post create an instance which the form params will be bound to
    @ModelAttribute("orderForm")
    public OrderForm getOrderForm(){
        OrderForm orderForm = new OrderForm();
        List<Exam> exams = examService.findAllExams();

        for(Exam exam : exams){
            orderForm.getAllAvailableExams.add(new ExamWrapper(exam));
        }

        return orderForm;
    }
}

In JSP use Sping support for binding to indexed properties:

<form:form method="POST" modelAttribute="orderForm" class="form-horizontal form-label-left">
    <c:forEach items="${orderForm.allAvailableExams}" var="exam" varStatus="status">
        <tr>
            <th scope="row"><input name="allAvailableExams[${status.index}].selected" 
                     type="checkbox" class="flat"></th>
        </tr>
    </c:forEach>

        <form:select path="selectedWrapper">
        <form:options items="${availableWrappers}" 
               itemValue="somePropertyOfXOptionPrintWrapper " 
               itemLabel="somePropertyOfXOptionPrintWrapper " />
        </form:select>
</form>

I obviously haven't been able to try all of this but think it should all be okay.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the reply Alan, I'll give this a shot and let you know how it works out.
What did you want to know?
In the same form, I need to populate a dropdown list, and allow the user to select one option, and post it back with the rest of the info. How would I set this up with the code you provided above?
I tried to add a "private List<XOptionPrintWrapper> allAvailableOptionPrints = new ArrayList<>();" to the form, similarly to the OrderForm class. I populated it like you did in the getOrderForm method. But when it comes to getting the data to post, I guess I'm not sure what to do.
See updates to GET method in Controller, OrderForm and JSP form. To get this to work you will need to register a simple converter that will auto-convert the id of the selected form option to the relevant type (e.g. by database lookup). docs.spring.io/spring/docs/current/spring-framework-reference/…
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.