javascript - 删除行并提交空指针异常后使用javascript的struts2动态html表

标签 javascript html jsp struts2 ognl

我是 Struts2 网络开发的初学者。为了学习,我正在开发一个食谱商店应用程序。我可以使用 JDBC 将 CRUD 基础 Material 导入到 MySQL 数据库中。现在我有一个 JSP 页面,用户可以在其中从基础 Material 创建配料以形成新配方。在我的程序中,一种成分是一种基础 Material ,它具有数量和单位名称集。 (在用户创建基础 Material 之前,他可以在默认单位名称(g、dkg、kg)旁边指定一个额外的单位名称(例如杯子、茶匙、玻璃杯等)。

我有一个 Struts2 doubleselect,其中第一个选择包含基本 Material 名称,第二个选择包含属于所选基本 Material 的相关单元名称。我也有数量输入文本字段。 从这 3 个输入,我可以创建一行新成分并将其动态添加到 JSP 中的 HTML 表中。表中的输入是附加到可见单元格值之后的单元格的隐藏文本输入。它们是隐藏的,因为我不希望它们是可编辑的,并且禁用不可编辑的文本字段不允许提交它们的值。此外,每一行在创建时都会获得一个删除 anchor 。每次删除后,都会重新计算隐藏输入元素名称中的索引。 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: stacktrace 它对我说,当我试图打印该名称进行测试时,它没有在成分列表中找到该成分的名称(已删除的名称) 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)



Edit 2: Output of the first line of the execution method of SaveRecipeAction action, in case of first deleting the middle ingredient from the sample 3-rows-long table and then submitting the form:

a hozzávalók lista mérete: 3

that is, the "size of the ingredient-list is: 3"

Edit 3: After modifying the index-recalculate block with alert(inp0.length) it will output 1 two times.

            //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。

最佳答案

不行,你应该改一下这段代码

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 表达式,其索引属性名称适合填充操作。

关于javascript - 删除行并提交空指针异常后使用javascript的struts2动态html表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21072280/

相关文章:

javascript - 检测视口(viewport)方向,如果方向是纵向显示警告消息,建议用户使用说明

jquery - jQuery 的 'Selectmenu' 的自定义样式

javascript - 禁用 HTML 中表单的所有元素(提交按钮除外)?

java - JSP 用户点击计数器

javascript - 如何从 html Canvas 上的tileset工作表多次绘制特定的tileset而不是整个图像

javascript - 即使表单具有有效值,Angular 4 按钮也会被禁用

javascript - res.render 中的 Express-Node JS Handlebars 模板 : How to iterate?

xml - 如何在 XHTML 中使用 HTML5 数据属性?

java - 在 JSP 中从 JavaScript/jQuery 调用后端 Java 方法

java - jsp servlet 应用程序中动态表上的编辑按钮