我有一个候选人模型和一些其他模型,如学校,学位,技能,工作岗位等,与候选人模型有一对多的关系。
我能够创建一个创建表单(创建新候选人的表单),可以使用javascript / jquery动态创建表单字段。用户可以通过单击“添加另一个按钮”来添加更多输入字段。表单提交给控制器,所有数据都可以完美地添加到数据库中。
问题与编辑表单有关。编辑表单与创建表单几乎相同,只是它在数据库的表单字段中预先填充了数据。当我将此表单提交给控制器中的更新功能时,它会更新错误。当我返回编辑表单以查看它是否正常工作时,会有随机输入字段,其中包含空白值,但数据会被编辑。
我对游戏框架相当新,可能我做错了。我使用Codeigniter在PHP中做了类似的事情,它运行良好。可能是因为Codeigniter不使用Ebean或JPA。
下面是我写的一些代码,所以我的问题更容易理解。我只会包括候选模型和学校模型,因为其他模型类似于学校模型。另外,我包含了生成动态表单字段的js代码。
非常感谢任何帮助!
Candidate.java(model):
@Entity
public class Candidate extends Model {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long id;
public String firstName;
public String lastName;
public boolean sponsorshipStatus;
public boolean proceedToInterview;
@Column(columnDefinition = "TEXT")
public String text;
@OneToMany(cascade = CascadeType.ALL)
public List<School> schools;
@OneToMany(cascade = CascadeType.ALL)
public List<Degree> degrees;
@OneToMany(cascade = CascadeType.ALL)
public List<Document> documents;
@OneToMany(cascade = CascadeType.ALL)
public List<JobPosition> jobPositions;
@OneToMany(cascade = CascadeType.ALL)
public List<Skill> skills;
/**
* Generic query helper for entity Candidate with id Long
*/
public static Model.Finder<Long, Candidate> find = new Model.Finder<Long, Candidate>(Long.class, Candidate.class);
public static Page<Candidate> page(int page, int pageSize, String sortBy, String order, String filter) {
return find.where()
.ilike("first_name", "%" + filter + "%")
.orderBy(sortBy + " " + order)
.findPagingList(pageSize)
.setFetchAhead(false).getPage(page);
}
}
School.java(model):
@Entity
public class School extends Model {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
public String schoolName;
@ManyToOne
public Candidate candidate;
/**
* Generic query helper for entity Company with id Long
*/
public static Model.Finder<Long, School> find = new Model.Finder<Long, School>(
Long.class, School.class);
}
Application.java(controller):
public class Application extends Controller {
public static Result index() {
return ok(index.render("Your new application is ready."));
}
/*display a list of candiates*/
public static Result list(int page, String sortBy, String order, String filter) {
return ok(views.html.list.render(Candidate.page(page, 10, sortBy, order, filter), sortBy, order, filter));
}
/**
* Display the 'edit form' of a existing Candidate.
*
* @param id
* Id of the candidate to edit
*/
public static Result edit(Long id) {
Form<Candidate> candidateForm = form(Candidate.class).fill(Candidate.find.byId(id));
Candidate candidate = Candidate.find.byId(id);
return ok(views.html.editForm.render(id, candidateForm, candidate));
}
/**
* Handle the 'edit form' submission
*
* @param id
* Id of the computer to edit
*/
public static Result update(Long id) {
RequestBody request_body = request().body();
MultipartFormData body = request_body.asMultipartFormData();
// MultipartFormData FilePart object list
List<FilePart> files = body.getFiles();
List<Document> docs = new ArrayList<Document>();
for (FilePart file : files) {
String fileName = file.getFilename();
// Use Java.IO File class to move file in desired location
File f = file.getFile();
File nf = new File("\\\\WL-MSAMARITONI\\Images", fileName);
f.renameTo(nf);
Document doc;
doc = new Document();
doc.docName = fileName;
doc.filePath = nf.getAbsolutePath();
docs.add(doc);
}
Form<Candidate> candidateForm = form(Candidate.class).bindFromRequest();
Candidate updateCandidate = candidateForm.get();
updateCandidate.documents = docs;
/*while(updateCandidate.degrees.remove(null));
while(updateCandidate.jobPositions.remove(null));
while(updateCandidate.schools.remove(null));
while(updateCandidate.skills.remove(null));
while(updateCandidate.documents.remove(null));
*/
updateCandidate.update(id);
updateCandidate.save();
flash("success", "Candidate " + candidateForm.get().firstName + " has been created");
return redirect("/candidate/" + id);
}
/**
* Display the 'new candidate form'.
*/
public static Result create() {
Form<Candidate> candidateForm = form(Candidate.class);
return ok(views.html.createForm.render(candidateForm));
}
/**
* Handle the 'new candidate form' submission
*/
public static Result save() {
RequestBody request_body = request().body();
MultipartFormData body = request_body.asMultipartFormData();
// MultipartFormData FilePart object list
List<FilePart> files = body.getFiles();
List<Document> docs = new ArrayList<Document>();
for (FilePart file : files) {
String fileName = file.getFilename();
// Use Java.IO File class to move file in desired location
File f = file.getFile();
File nf = new File("\\\\WL-MSAMARITONI\\Images", fileName);
f.renameTo(nf);
Document doc;
doc = new Document();
doc.docName = fileName;
doc.filePath = nf.getAbsolutePath();
docs.add(doc);
}
Form<Candidate> candidateForm = form(Candidate.class).bindFromRequest();
Candidate newCandidate = candidateForm.get();
newCandidate.documents = docs;
newCandidate.save();
flash("success", "Computer " + candidateForm.get().firstName + " has been created");
return redirect("/candidates/new");
}
}
createForm.scala.html(view):
@(candidateForm: Form[Candidate])
@import helper._
@main("Create Candidate"){
<h1>Add a New Candidate</h1>
@form(routes.Application.save(), args= 'class -> "form-horizontal",'enctype -> "multipart/form-data") {
<div class="row">
<fieldset class="span6">
<legend>
<h3> Add Candidate Info</h3>
</legend>
@inputText(candidateForm("firstName"), '_label -> "First Name")
@inputText(candidateForm("lastName"), '_label -> "Last Name")
@checkbox(candidateForm("sponsorshipStatus"), '_label -> "Sponsorship Status")
@checkbox(candidateForm("proceedToInterview"), '_label -> "Proceed to Interview")
@textarea(candidateForm("text"), args = 'rows -> 3, 'cols -> 50, '_label -> "Notes")
</fieldset>
<fieldset class="span6">
<legend>
<h3>Add Schools</h3>
</legend>
<div id="schools" class="control-group">
<div class="school hidden_form_default">
<input id="chk_school" type="checkbox">
<input disabled="disabled" id="school" name="schools[].schoolName" placeholder="Enter School">
</div>
<div class="school">
<input id="chk_school_0" type="checkbox" value="1">
<input id="school_0" name="schools[0].schoolName" placeholder="Enter School" >
</div>
</div>
<button class="btn school_add_button">
Add Another
</button>
<button class="btn btn-danger school_delete_button">
Delete Checked
</button>
</fieldset>
</div>
<div class="row">
<fieldset class="span6">
<legend>
<h3>Add Degrees</h3>
</legend>
<div id="degrees" class="control-group">
<div class="degree hidden_form_default">
<input id="chk_degree" class="" type="checkbox" value="1">
<input id="degreeLevel" disabled="disabled" class="" name="degrees[].degreeLevel" placeholder="Degree Level" >
<input id="degreeFocus" disabled="disabled" class="" name="degrees[].degreeFocus" placeholder="Degree Focus" >
<input type="text" id="completedDate" disabled="disabled" class="datepicker input-small" name="degrees[].completedDate" placeholder="Completed" >
</div>
<div class= "degree">
<input id="chk_degree_0" class="" type="checkbox" value="1">
<input id="degreeLevel_0" class="" name="degrees[0].degreeLevel" placeholder="Degree Level" >
<input id="degreeFocus_0" class="" name="degrees[0].degreeFocus" placeholder="Degree Focus" >
<input type="text" id="completedDate_0" class="datepicker input-small" name="degrees[0].completedDate" placeholder="Completed" >
</div>
</div>
<button class="btn degree_add_button">
Add Another
</button>
<button class="btn btn-danger degree_delete_button">
Delete Checked
</button>
</fieldset>
<fieldset class="span6">
<legend>
<h3>Add Skills</h3>
</legend>
<div id="skills" class="control-group">
<div class="skill hidden_form_default">
<input id="chk_skill" type="checkbox">
<input disabled="disabled" id="skill" name="skills[].skill" class="clearMeFocus" placeholder="Enter skill">
</div>
<div class="skill">
<input id="chk_skill_0" type="checkbox" value="1">
<input id="skill_0" name="skills[0].skill" class="clearMeFocus" placeholder="Enter skill" >
</div>
</div>
<button class="btn skill_add_button">
Add Another
</button>
<button class="btn btn-danger skill_delete_button">
Delete Checked
</button>
</fieldset>
</div>
<div class="row">
<fieldset class="span6">
<legend>
<h3>Add Desired Positions</h3>
</legend>
<div id="positions" class="control-group">
<div class="position hidden_form_default">
<input id="chk_position" class="" type="checkbox" value="1">
<input id="position" disabled="disabled" class="" name="jobPositions[].position" placeholder="Enter Position" >
</div>
<input id="chk_position_0" class="" type="checkbox" value="1">
<input id="positionLevel_0" class="" name="jobPositions[0].position" placeholder="Enter Position" >
</div>
<button class="btn position_add_button">
Add Another
</button>
<button class="btn btn-danger position_delete_button">
Delete Checked
</button>
</fieldset>
<fieldset class="span6">
<legend>
<h3>Add Documents</h3>
</legend>
<div id="documents" class="control-group">
<div class="document hidden_form_default">
<input id="chk_document" type="checkbox">
<input type="file" disabled="disabled" id="document" name="documents[]">
</div>
<div class="document">
<input id="chk_document_0" type="checkbox" value="1">
<input id="document_0" type="file" name="documents[0]" >
</div>
</div>
<button class="btn document_add_button">
Add Another
</button>
<button class="btn btn-danger document_delete_button">
Delete Checked
</button>
</fieldset>
</div>
<button type="submit" class="btn btn-primary control">
Add Candidate
</button>
}
}
editForm.scala.html(view)
@(id: Long, candidateForm: Form[Candidate], candidate: Candidate)
@import helper._
@main("Edit Candidate") {
<h1>Edit Candidate</h1>
@form(routes.Application.update(id), args= 'class -> "form-horizontal",'enctype -> "multipart/form-data", 'id -> "updateForm") {
<div class="row">
<fieldset class="span6">
<legend>
<h3> Edit Candidate Info</h3>
</legend>
@inputText(candidateForm("firstName"), '_label -> "First Name")
@inputText(candidateForm("lastName"), '_label -> "Last Name")
@checkbox(candidateForm("sponsorshipStatus"), '_label -> "Sponsorship Status")
@checkbox(candidateForm("proceedToInterview"), '_label -> "Proceed to Interview")
@textarea(candidateForm("text"), args = 'rows -> 3, 'cols -> 50, '_label -> "Notes")
</fieldset>
<fieldset class="span6">
<legend>
<h3>Add Schools</h3>
</legend>
<div id="schools" class="control-group">
<div class="school hidden_form_default">
<input id="chk_school" type="checkbox">
<input disabled="disabled" id="school" name="schools[].schoolName" placeholder="Enter School">
</div>
@for(school <- candidate.schools) {
<div class="school">
<input id="chk_school_@school.id" type="checkbox" value="@school.id">
<input id="school_@school.id" name="schools[@school.id].schoolName" value="@school.schoolName" placeholder="Enter School" >
<input type="hidden" id="schoolId_@school.id" class="" name="schools[@school.id].id" value="@school.id">
</div>
}
<div class="school new_field_div">
<input id="chk_school_0" type="checkbox" value="1">
<input id="school_0" name="schools[0].schoolName" placeholder="Enter School" >
</div>
</div>
<button class="btn school_add_button">
Add Another
</button>
<button class="btn btn-danger school_delete_button">
Delete Checked
</button>
</fieldset>
</div>
<div class="row">
<fieldset class="span6">
<legend>
<h3>Add Degrees</h3>
</legend>
<div id="degrees" class="control-group">
<div class="degree hidden_form_default">
<input id="chk_degree" class="" type="checkbox" value="1">
<input id="degreeLevel" disabled="disabled" class="" name="degrees[].degreeLevel" placeholder="Degree Level" >
<input id="degreeFocus" disabled="disabled" class="" name="degrees[].degreeFocus" placeholder="Degree Focus" >
<input type="text" id="completedDate" disabled="disabled" class="datepicker input-small" name="degrees[].completedDate" placeholder="Completed" >
</div>
@for(degree <- candidate.degrees) {
<div class ="degree">
<input id="chk_degree_@degree.id" class="" type="checkbox" value="@degree.id">
<input id="degreeLevel_@degree.id" class="" name="degrees[@degree.id].degreeLevel" value="@degree.degreeLevel" placeholder="Degree Level" >
<input id="degreeFocus_@degree.id" class="" name="degrees[@degree.id].degreeFocus" value="@degree.degreeFocus" placeholder="Degree Focus" >
<input type="hidden" id="degreeId_@degree.id" class="" name="degrees[@degree.id].id" value="@degree.id">
<input type="text" id="completedDate_@degree.id" class="datepicker input-small" name="degrees[@degree.id].completedDate" value="@degree.completedDate" placeholder="Completed" >
</div>
}
<div class ="degree new_field_div">
<input id="chk_degree_0" class="" type="checkbox" value="1">
<input id="degreeLevel_0" class="" name="degrees[0].degreeLevel" placeholder="Degree Level" >
<input id="degreeFocus_0" class="" name="degrees[0].degreeFocus" placeholder="Degree Focus" >
<input type="text" id="completedDate_0" class="datepicker input-small" name="degrees[0].completedDate" placeholder="Completed" >
</div>
</div>
<button class="btn degree_add_button">
Add Another
</button>
<button class="btn btn-danger degree_delete_button">
Delete Checked
</button>
</fieldset>
<fieldset class="span6">
<legend>
<h3>Add Skills</h3>
</legend>
<div id="skills" class="control-group">
<div class="skill hidden_form_default">
<input id="chk_skill" type="checkbox">
<input disabled="disabled" id="skill" name="skills[].skill" class="clearMeFocus" placeholder="Enter skill">
</div>
@for(skill <- candidate.skills) {
<div class="skill">
<input id="chk_skill_@skill.id" type="checkbox" value="@skill.id">
<input id="skill_@skill.id" name="skills[@skill.id].skill" class="clearMeFocus" value="@skill.skill" placeholder="Enter skill" >
<input type="hidden" id="skillId_@skill.id" class="" name="skills[@skill.id].id" value="@skill.id">
</div>
}
<div class="skill new_field_div">
<input id="chk_skill_0" type="checkbox" value="1">
<input id="skill_0" name="skills[0].skill" class="clearMeFocus" placeholder="Enter skill" >
</div>
</div>
<button class="btn skill_add_button">
Add Another
</button>
<button class="btn btn-danger skill_delete_button">
Delete Checked
</button>
</fieldset>
</div>
<div class="row">
<fieldset class="span6">
<legend>
<h3>Add Desired Positions</h3>
</legend>
<div id="positions" class="control-group">
<div class="position hidden_form_default">
<input id="chk_position" class="" type="checkbox" value="1">
<input id="position" disabled="disabled" class="" name="jobPositions[].position" placeholder="Enter Position" >
</div>
@for(position <- candidate.jobPositions) {
<div class="position">
<input id="chk_position_@position.id" class="" type="checkbox" value="@position.id">
<input id="positionLevel_@position.id" class="" name="jobPositions[@position.id].position" value="@position.position" placeholder="Enter Position" >
<input type="hidden" id="positionId_@position.id" class="" name="jobPositions[@position.id].id" value="@position.id">
</div>
}
<div class="position new_field_div">
<input id="chk_position_0" class="" type="checkbox" value="1">
<input id="positionLevel_0" class="" name="jobPositions[0].position" placeholder="Enter Position" >
</div>
</div>
<button class="btn position_add_button">
Add Another
</button>
<button class="btn btn-danger position_delete_button">
Delete Checked
</button>
</fieldset>
<fieldset class="span6">
<legend>
<h3>Add Documents</h3>
</legend>
<div id="documents" class="control-group">
<div class="document hidden_form_default">
<input id="chk_document" type="checkbox">
<input type="file" disabled="disabled" id="document" name="documents[]">
</div>
@for(document <- candidate.documents) {
<div class="document">
<input id="chk_document_@document.id" type="checkbox" value="@document.id">
<input type="hidden" id="documentId_@document.id" class="" name="documents[@document.id].id" value="@document.id">
<p>@document.docName</p>
</div>
}
<div class="document new_field_div">
<input id="chk_document_0" type="checkbox" value="1">
<input id="document_0" type="file" name="documents[0]" >
</div>
</div>
<button class="btn document_add_button">
Add Another
</button>
<button class="btn btn-danger document_delete_button">
Delete Checked
</button>
</fieldset>
</div>
<div class="actions">
<input type="submit" value="Save this Candidate" class="btn primary">
or <a href="@routes.Application.list()" class="btn">Cancel</a>
</div>
}
}
main.js:
$(function () {
var delete_form_url = "/school/delete"
add_another_form_field('.school_add_button', '.school', '#schools', 'schoolName', '.school_delete_button', delete_form_url);
add_another_form_field('.degree_add_button', '.degree', '#degrees', 'degreeName', '.degree_delete_button', delete_form_url);
add_another_form_field('.skill_add_button', '.skill', '#skills', 'skillName', '.skill_delete_button', delete_form_url);
add_another_form_field('.position_add_button', '.position', '#positions', 'positionName', '.position_delete_button', delete_form_url);
add_another_form_field('.document_add_button', '.document', '#documents', 'documentName', '.document_delete_button', delete_form_url);
$('.datepicker').datepicker();
$("#updateForm").submit(function (event) {
$(".new_field_div").each(function () {
var isNull = false;
var new_field_inputs = $(this).find("input:text");
new_field_inputs.each(function (index, element) {
console.log($(element).val());
if ($(element).val()) {
isNull = false;
return false;
} else {
isNull = true;
}
});
if (isNull) {
$(this).remove();
}
});
});
});
function add_another_form_field(add_button_class, child_div_class, parent_element_id, field_name, delete_button_class, delete_form_url) {
var num = 1;
$(add_button_class).on("click", function (event) {
event.preventDefault();
var cloned_fields = $(child_div_class + ":first").clone();
console.log(cloned_fields);
cloned_fields.find('input,textarea,select').attr('id', function (i, val) {
if (val !== undefined) {
return val + '_' + num;
}
});
cloned_fields.find('label').attr('for', function (i, val) {
if (val !== undefined) {
return val + '_' + num;
}
});
cloned_fields.find('input,textarea,select').attr('name', function (i, val) {
if (val !== undefined) {
return val.replace(/\[\]/, "[" + num + "]");
}
});
cloned_fields.removeClass("hidden_form_default");
cloned_fields.addClass("new_field_div");
cloned_fields.find('input').prop('disabled', false);
cloned_fields.appendTo(parent_element_id);
num++;
// clear the new inputs on click
// clear input on focus
$(".clearMeFocus").focus(function () {
if ($(this).val() == $(this).attr("title")) {
clearMePrevious = $(this).val();
$(this).val("");
}
});
// if field is empty afterward, add text again
$(".clearMeFocus").blur(function () {
if ($(this).val() == "") {
$(this).val(clearMePrevious);
}
});
});
$(delete_button_class).click(function (event) {
event.preventDefault();
var form = $(this).parents('form');
var table_name = form.find('input[name="table_name"]').val();
$(child_div_class).find('input[type="checkbox"]:checked:enabled').each(function (index, child) {
table_row_id = $(child).val();
if (table_row_id) {
$.post(delete_form_url, {
table_name: table_name,
table_row_id: table_row_id
});
}
// delete from view
$(child).parents().remove(child_div_class);
});
});
}