【问题标题】:struts2 dynamic html table with javascript after removing row and submitting nullpointerexception删除行并提交nullpointerexception后带有javascript的struts2动态html表
【发布时间】:2014-01-12 07:21:03
【问题描述】:

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

我有一个 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: stacktrace 它对我说,在我尝试打印该名称进行测试时,它在成分列表中找不到成分的名称(已删除的成分) SaveRecipeAction 中的用途。这意味着,在提交表格后,它会尝试设置错误的成分数量。它尝试根据表格的“早期/删除前状态”-长度将成分数量设置到列表中。

说,如果我在附加的屏幕截图中获得了 3 个项目,并删除了中间的项目,然后提交表单,它将只打印第一个元素。首先我想,这背后的原因是因为当我测试程序时,我导航回上一页,到那时,一旦我应该删除更深的内容(在配方 bean 中的列表中),列表已经提交。这甚至不应该发生,因为 JS 脚本只是客户端。但是即使在重新启动tomcat,重新加载页面后也会出现此问题。

Struts 问题报告 Struts 检测到一个未处理的异常: 留言: 文件:actions/SaveRecipeAction.java 行号:21 堆栈跟踪 java.lang.NullPointerException actions.SaveRecipeAction.execute(SaveRecipeAction.java:21) sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法) 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ók lista mérete: 3

即“配料表的大小为:3”

编辑3:alert(inp0.length)修改index-recalculate块后会输出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),根本不输出警报框。

【问题讨论】:

  • 发布完整的堆栈跟踪。
  • execute方法第一行的输出是什么?
  • 你能打印alert(inp0.length)的输出吗?
  • 现在,如果您将inp0 更改为inp0[0] 和其他类似的变量会怎样?

标签: javascript html jsp struts2 ognl


【解决方案1】:

不,你应该改变这段代码

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

【讨论】:

  • 谢谢!看来,它可以正常工作。你能解释一下为什么我的解决方案不好?为什么我必须以这种方式达到所选元素的名称属性?
  • getElementsByTagName 函数返回一个数组对象,即使其中只有一个元素,您也应该使用[] 访问它。
  • 好的。所以当我设置inp0.name 时,我实际上在做的是创建一个个人的、自己的属性而不是设置实名属性?
  • 这是一个javascript错误,因为inp0中没有name这样的属性。如果您放置alert(inp0.name);,它将输出undefined
  • 好的,我明白了!谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-04
  • 1970-01-01
  • 2015-01-28
  • 2016-04-22
  • 2021-08-28
相关资源
最近更新 更多