2014-01-12 47 views
3

我是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。

+0

發佈一個完整的stacktrace。 –

+0

第一行'execute'方法的輸出是什麼? –

+0

你可以打印'alert(inp0.length)'輸出嗎? –

回答

0

不,你應該改變的碼

for(var i=0; i < table.rows.length; i++) { 
    var inp0 = table.rows[i].cells[0].getElementsByTagName("input"); 
    inp0[0].name = "recipe.ingredients["+i+"].ingredientName"; 
    var inp1 = table.rows[i].cells[1].getElementsByTagName("input"); 
    inp1[0].name="recipe.ingredients["+i+"].ingredientUnitName"; 
    var inp2 = table.rows[i].cells[2].getElementsByTagName("input"); 
    inp2[0].name="recipe.ingredients["+i+"].ingredientQuantity"; 
    table.rows[i].cells[4].innerHTML = inp0[0].name; 
} 

它將設置輸入name屬性OGNL用合適的填充作用索引屬性名的表達式。

+0

謝謝!看起來,它的工作原理應該如此。你能解釋爲什麼我的解決方案不好嗎?爲什麼我必須以這種方式覆蓋選定元素的名稱屬性? – gllo

+0

'getElementsByTagName'函數返回一個數組對象,即使只有一個元素存在,您也應該使用'[]'來訪問它。 –

+0

好的。所以當我設置'inp0.name'時,我真正在做的是創建一個個人屬性,而不是設置實名屬性? – gllo