我是Struts2,網頁開發初學者。爲了學習,我正在開發一個配方商店應用程序。我可以使用JDBC將基礎材料刪除到MySQL數據庫中。現在我有一個JSP頁面,用戶可以從基礎材料創建配料以形成新配方。在我的程序中,一種成分是一種具有數量和單位名稱的基礎材料。 (在用戶創建基礎材料之前,他可以在默認單元名稱(g,dkg,kg)旁邊指定一個附加單元名稱(例如杯子,茶匙,玻璃等))。struts2動態HTML表格與javascript後刪除行並提交nullpointerexception
我有一個Struts2 doubleselect
其中第一個選擇保存基礎材料名稱,第二個保存屬於所選基礎材料的相關單元名稱。我也有數量輸入文本框。 從這3個輸入中,我可以創建並向JSP中的HTML表格動態添加一行新成分。表格中的輸入是在可見單元格值之後附加到單元格的隱藏文本輸入。它們是隱藏的,因爲我不希望它們是可編輯的,禁用的不可編輯文本框不允許提交它們的值。此外,每一行都會在創建時獲得一個刪除錨點。隱藏輸入元素名稱中的索引在每次刪除後都會重新計算。 Click me for the view of new recipe creation JSP
我可以提交輸入,在沒有刪除行的情況下在結果JSP中使用struts2操作接受並打印它,或者刪除的行是最後一個。否則,如果我在動態成分列表的中間或開頭刪除了一行,我會在提交表單後得到NullPointerException
。如果我刪除最後一行並在之後提交,它會完美工作,就像我沒有刪除任何行一樣。
那麼,我在這裏錯過了什麼?我沒有正確刪除表格行嗎?我該怎麼做?
的newrecipe.jsp
相關部分:
<script>
function addRow(tableID) {
//get material quantity value from quantity field
var materialQuantity = document.getElementById("materialQuantity");
/*VALIDATION OF QUANTITY FIELD: must be number, cannot be empty
[better to turn off validation until development is ongoing,
this is just set here for clarity while asking for help]*/
if (materialQuantity.value == "") {
alert("Add meg a hozzávaló mennyiségét!");
}
else if (isNaN(materialQuantity.value) == true) {
alert("A mennyiség csak szám lehet!");
}
else {
//get selected material name value
var selectedMaterial = $("#recipeSelect1 :selected").text()
//get selected unit name value
var selectedUnitName = $("#recipeSelect2 :selected").text()
//get table object
var table = document.getElementById(tableID);
var rowCount = table.rows.length;
var row = table.insertRow(rowCount);
var counts=rowCount;
/*create the cells of a new ingredient dynamic table row and
insert, append a hidden text input into it beside the visible values*/
var cell1 = row.insertCell(0);
var ingredientName = document.createElement("input");
ingredientName.type = "hidden";
ingredientName.name="recipe.ingredients["+counts+"].ingredientName";
ingredientName.value=selectedMaterial;
$(cell1).append(ingredientName);
$(cell1).append(selectedMaterial);
var cell2 = row.insertCell(1);
var ingredientUnitName = document.createElement("input");
ingredientUnitName.type = "hidden";
ingredientUnitName.name="recipe.ingredients["+counts+"].ingredientUnitName";
ingredientUnitName.value=selectedUnitName;
ingredientUnitName.className="materialFields";
$(cell2).append(ingredientUnitName);
$(cell2).append(selectedUnitName);
var cell3 = row.insertCell(2);
var ingredientQuantity = document.createElement("input");
ingredientQuantity.type = "hidden";
ingredientQuantity.name="recipe.ingredients["+counts+"].ingredientQuantity";
ingredientQuantity.value=materialQuantity.value;
ingredientQuantity.className="materialFields";
$(cell3).append(ingredientQuantity);
$(cell3).append(materialQuantity.value);
//create the dynamic anchor element for deletion of table row
var cell4 = row.insertCell(3);
var delIR = document.createElement("a");
delIR.href = "#";
delIR.className = "delIR";
delIR.innerHTML="Törlés";
delIR.onclick = function() {
//delete row
$(cell4).closest('tr').remove();
//recalculate name index after deletion of a row
for(var i=0; i < table.rows.length; i++) {
var inp0 = table.rows[i].cells[0].getElementsByTagName("input");
inp0.name = "recipe.ingredients["+i+"].ingredientName";
var inp1 = table.rows[i].cells[1].getElementsByTagName("input");
inp1.name="recipe.ingredients["+i+"].ingredientUnitName";
var inp2 = table.rows[i].cells[2].getElementsByTagName("input");
inp2.name="recipe.ingredients["+i+"].ingredientQuantity";
table.rows[i].cells[4].innerHTML = inp0.name;
}
};
$(cell4).append(delIR);
//print ingredient name for test purposes
var cell5 = row.insertCell(4);
$(cell5).append(ingredientName.name);
//empty quantity field after creation of row
materialQuantity.value = "";
}
}
</script>
</head>
<body>
<h1><s:property value="#materialTitle"/></h1>
<s:form action="saverecipe" method="post" id="recipeForm" theme="simple">
<div class="recipeLeftDiv">
<div class="recipeNameDiv">
<label class="recipeLabel" for="recipeNameField">Recept neve:</label>
<s:textfield
cssClass="recipeNameField"
id="recipeNameField"
name="recipe.recipeName"
/>
</div>
<s:doubleselect
id="recipeSelect1"
cssClass="recipeSelect1"
name="recipeSelect1"
list="materialNameUnitNameMap.keySet()"
doubleId="recipeSelect2"
doubleCssClass="recipeSelect2"
doubleName="recipeSelect2"
doubleList="materialNameUnitNameMap.get(top)"
theme="css_xhtml"
>
</s:doubleselect>
<s:textfield
id="materialQuantity"
cssClass="materialFields"
>
</s:textfield>
<button type="button" onclick="addRow('ingredientTable')">Hozzáadás</button>
<table id="ingredientTable" class="ingredientTable">
</table>
</div>
SaveRecipeAction.java
:
package actions;
import com.opensymphony.xwork2.ActionSupport;
import beans.Ingredient;
import beans.Recipe;
public class SaveRecipeAction extends ActionSupport {
private Recipe recipe;
public Recipe getRecipe() {
return recipe;
}
public void setRecipe(Recipe recipe) {
this.recipe = recipe;
}
public String execute() {
System.out.println("a hozzávalók lista mérete: "+recipe.getIngredients().size());
for (Ingredient ing:recipe.getIngredients()) {
System.out.print(ing.getIngredientName()+" ");
System.out.print(ing.getIngredientQuantity()+" ");
System.out.println(ing.getIngredientUnitName());
}
return SUCCESS;
}
}
Recipe.java
:
package beans;
import java.util.List;
public class Recipe {
private String recipeName;
private List<Ingredient> ingredients;
private String recipeDesc;
public String getRecipeName() {
return recipeName;
}
public void setRecipeName(String recipeName) {
this.recipeName = recipeName;
}
public List<Ingredient> getIngredients() {
return ingredients;
}
public void setIngredients(List<Ingredient> ingredients) {
this.ingredients = ingredients;
}
public String getRecipeDesc() {
return recipeDesc;
}
public void setRecipeDesc(String recipeDesc) {
this.recipeDesc = recipeDesc;
}
}
Ingredient.java
:
package beans;
public class Ingredient {
private double ingredientQuantity;
private String ingredientName;
private String ingredientUnitName;
public String getIngredientName() {
return ingredientName;
}
public void setIngredientName(String ingredientName) {
this.ingredientName = ingredientName;
}
public double getIngredientQuantity() {
return ingredientQuantity;
}
public void setIngredientQuantity(double ingredientQuantity) {
this.ingredientQuantity = ingredientQuantity;
}
public String getIngredientUnitName() {
return ingredientUnitName;
}
public void setIngredientUnitName(String ingredientUnitName) {
this.ingredientUnitName = ingredientUnitName;
}
}
EDIT1:堆棧跟蹤這對我說,雖然我想它不會發現在配料表中的成分(刪除一個)的名稱在SaveRecipeAction
中打印該名稱用於測試目的。這意味着,在提交表單時,它會嘗試設置錯誤的配料數量。它試圖根據表格的「更早/更早刪除狀態」來設置成分數量。
說,如果我在成分列表中獲得了3個項目,就像我附加的截圖一樣,刪除中間的一個,然後提交表單,它將只打印第一個元素。首先,我想,這背後的原因是因爲當我測試程序時,我回到上一頁,到那個時候,我應該刪除更深的列表(在配方bean的列表中)。這應該不會發生,因爲JS腳本只是客戶端。但即使重新啓動了tomcat,頁面重新加載後也會出現此問題。
Struts Problem Report Struts has detected an unhandled exception: Messages: File: actions/SaveRecipeAction.java Line number: 21 Stacktraces java.lang.NullPointerException actions.SaveRecipeAction.execute(SaveRecipeAction.java:21) sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) java.lang.reflect.Method.invoke(Method.java:606) com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:450) com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:289) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:252) org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:256) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:167) com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:265) org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:68) com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:138) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:239) com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:239) com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:191) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:73) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:91) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:252) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:100) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:141) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:145) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171) com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:161) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:193) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:189) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246) org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54) org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:563) org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77) org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953) org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041) org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603) org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) java.lang.Thread.run(Thread.java:744)
編輯2:SaveRecipeAction動作的執行方法的第一行的輸出,在首先從樣品3行長的表中刪除的中間成分,然後提交表單的情況下:
一個hozzávalókLISTA大小:3
即, 「成分列表的尺寸是:3」
編輯3:在使用alert(inp0.length)
修改索引重新計算塊後,它將輸出1
兩次。
//recalculate name index after deletion of a row
for(var i=0; i < table.rows.length; i++) {
var inp0 = table.rows[i].cells[0].getElementsByTagName("input");
inp0.name = "recipe.ingredients["+i+"].ingredientName";
alert(inp0.length);
var inp1 = table.rows[i].cells[1].getElementsByTagName("input");
inp1.name="recipe.ingredients["+i+"].ingredientUnitName";
var inp2 = table.rows[i].cells[2].getElementsByTagName("input");
inp2.name="recipe.ingredients["+i+"].ingredientQuantity";
table.rows[i].cells[4].innerHTML = inp0.name;
}
編輯4:alert(inp0[0].length)
輸出 「未定義」。 inp0[1].length
和上面的索引(檢查直到索引5
),根本不輸出alertbox。
發佈一個完整的stacktrace。 –
第一行'execute'方法的輸出是什麼? –
你可以打印'alert(inp0.length)'輸出嗎? –