I have a "Question" entity that has a "Answer" that has a list of "Alternatives" as below:
public class Question extends BaseEntity {
private String text;
private String sourceCode;
private String complement;
private Answer answer;
}
public class Answer extends BaseEntity{
private List<Alternative> alternatives;
}
I want to make a form to user populate with a list of questions. I read so many material and SO questions, but I cannot get how to do exactly what I want with forms. I know that I can do it in other manner using DynamicForms, this is not what I want. My idea is that it could be done this way:
But when I try to use the Answer object and it "Alternatives" a big NullPointerexception explodes on my face:
final Form<Question> questionForm = f.bindFromRequest();
final Question question = questionForm.get();
System.out.println(question);
System.out.println(question.getText());
System.out.println(question.getSourceCode());
System.out.println(question.getComplement());
//Nullpointer here:
final List<Alternative> alternatives =
question.getAnswer().getAlternatives();
alternatives.forEach(p -> System.out.println(p));
I miss more documentation and examples about it and other things related. Even the official website does not provide extensive examples. Especially when dealing with Java. This gives an idea that the framework is becoming obsolete or being overtaken by other technologies?
I use version 2.4.6 of Play.
解决方案
This is documented here, specifically at how to handle repeated values. Of course, documentation can always be improved. Please open an issue raising this problem. Also, this does not represent that the framework is becoming obsolete or being overtaken by others (in fact, Play 2.5 is close to being released and the community is growing).
Anyway, here is an comprehensive example about how to do a parent/child form like you described. Keep in mind that I didn't care about styling the forms.
Your domain hierarchy:
models/Question.java:
package models;
public class Question {
public String text;
public String sourceCode;
public String complement;
public Answer answer;
}
models/Answer.java:
package models;
import java.util.List;
public class Answer {
public List<Alternative> alternatives;
}
models/Alternative.java:
package models;
public class Alternative {
public boolean correct;
public String statement;
}
Controllers and routes:
Now, I have the following action that just returns the Question object and its children as a JSON (since we are just interested in how to submit this kind of data):
package controllers;
import models.Question;
import play.data.Form;
import play.libs.Json;
import play.mvc.*;
import views.html.*;
import static play.data.Form.form;
public class Application extends Controller {
public Result index() {
Form<Question> form = form(Question.class);
return ok(index.render(form));
}
public Result post() {
Form<Question> form = form(Question.class).bindFromRequest();
Question question = form.get();
return ok(Json.toJson(question));
}
}
GET / controllers.Application.index()
POST /save controllers.Application.post()
The form view:
Finally, we need to create the form that will populate and submit the data:
@(questionForm: Form[Question])
@main("Welcome to Play") {
@helper.form(action = routes.Application.post()) {
<h2>Question:</h2>
@helper.inputText(questionForm("text"))
@helper.inputText(questionForm("sourceCode"))
@helper.inputText(questionForm("complement"))
<h3>Answers:</h3>
@helper.repeat(questionForm("answer.alternatives"), min = 2) { alternative =>
@helper.checkbox(alternative("correct"))
@helper.inputText(alternative("statement"))
}
<button type="submit">Save</button>
}
}
Basically the form was created using the form helpers which will handle most of the aspects of how the form works (like showing errors, per instance). Special attention to the @helper.repeat tag: it will create the following markup (omitting irrelevant parts):