如何使用 jquery 从 asp.net 视图模型中动态删除行而不删除集合中的其余下一个对象

Posted

技术标签:

【中文标题】如何使用 jquery 从 asp.net 视图模型中动态删除行而不删除集合中的其余下一个对象【英文标题】:How to delete rows dynamically with jquery from an asp.net view model without deleting the rest of the next objects in the collection 【发布时间】:2016-12-18 09:36:21 【问题描述】:

据我了解,我遇到了索引问题。让我们从实际代码开始(这是整个页面代码中的一个片段,但与此类似的其他片段也无法正常工作):

创建通用对象部分视图的 cshtml 代码。

<div id="genericObjects">
@if (Model.GenericObjects != null && Model.GenericObjects.Any())

  for (int i = 0; i < Model.GenericObjects.Count; i++)
  
       Html.RenderPartial("_GenericObject", Model, new ViewDataDictionary(this.ViewData)   "GenericIndex", i  ); 
  

</div>

_GenericObject 部分视图:

model Noodle.Presentation.Models.MashupViewModel

@
   var f = Html.Bootstrap().Misc().GetBuilderFor(new Form().Type(FormType.Horizontal).LabelWidthMd(3));
    var index = (int)ViewBag.GenericIndex;


<div class="row generic">
  <h4>
    <span>Object</span>
    @Html.Bootstrap().Button().Class("remove-generic pull-right").PrependIcon("glyphicon glyphicon-trash").Text("")
  </h4>
  @f.FormGroup().TextBoxFor(s => Model.GenericObjects[index].Name).Label().ShowRequiredStar(false)
  @f.FormGroup().TextBoxFor(s => Model.GenericObjects[index].ForeignId).Label().ShowRequiredStar(false)
  @f.FormGroup().TextBoxFor(s => Model.GenericObjects[index].Value).Label().ShowRequiredStar(false)

</div>

调用局部视图的GetGenericObject方法:

public PartialViewResult GetGenericObject(int? index)
        
            ViewBag.GenericIndex = index;
            return PartialView("_GenericObject");
        

这里是添加(工作正常)和删除jquery方法:

$('#mashup-form').on('click', '.add-generic-object-btn', function () 
                var index = $('#genericObjects').children().length;
                $('<div>').load(genericObjPath + '?index=' + index, function () 
                    $('#genericObjects').append($(this).find('.generic')[0].outerHTML);
                    revalidate();
                );
            );

$('#mashup-form').on('click', '.remove-generic', function () 
                $(this).parent().parent().remove();
);

至于实际问题,假设我们有三个通用对象:

测试1 测试2 测试3

如果 TEST2 被删除,则不会提交其后出现的通用对象(尽管它们确实出现在页面上)。

基本上,页面上剩下的内容:

测试1, 测试3

模型中提交的内容:

测试1

其余的代码工作得很好。在提交时显示 c# 方法调用没有真正意义,因为它工作正常,但接收的模型中没有 TEST3 对象。

现在,这似乎是一个索引问题,我试图通过改变这样的代码来解决这个问题(这个想法来自另一个 *** 线程 [delete table row dynamically using jQuery in asp.net mvc):

 $('#mashup-form').on('click', '.remove-generic', function () 
   $(this).parent().parent().remove();
   var index = 0;
   var itemIndex = 0;
   $('#genericObjects').each(function () 
     var this_row = $(this);
     this_row.find('input[name$=".Name"]').attr('name', 'Model.GenericObjects[' + itemIndex + '].Name');
     this_row.find('input[name$=".ForeignId"]').attr('name', 'Model.GenericObjects[' + itemIndex + '].ForeignId');
     this_row.find('input[name$=".Value"]').attr('name', 'Model.GenericObjects[' + itemIndex + '].Value');
     itemIndex++;
   );
 );

结果相当奇怪。同样,如果我们有三个对象 TEST1 TEST2 TEST3,通过删除 TEST1 并提交,我只得到了 TEST2,我的意思是整个模型都被擦除了,但是这个对象(模型有更多对象,这只是页面的一个片段)。此外,如果 TEST2 被删除,则 TEST1 是视图模型中的唯一对象(其他所有对象均为空)。如果没有按下通用对象上的删除按钮,一切正常。

所以,如果您对如何解决此问题有任何想法,请告诉他们。此外,如果您可能需要其他一些信息或代码,请随时询问。诚然,我可能无法给予,但我会看看能做些什么。祝你好运!

解决问题后编辑:

我在下面标记了答案,其中涉及使用 EditorFor,作为已接受的答案,因为我发现它写得很好,并且可能对阅读这个问题并且根本无法使部分视图起作用的人有用。但是,就我而言,我可以使以前的系统正常工作。我犯了一个非常基本的愚蠢错误,只指定了局部视图之外的元素。所以这个:

$('#mashup-form').on('click', '.remove-generic', function () 
   $(this).parent().parent().remove();
   var index = 0;
   var itemIndex = 0;
   $('#genericObjects').each(function () 
     var this_row = $(this);
     this_row.find('input[name$=".Name"]').attr('name', 'Model.GenericObjects[' + itemIndex + '].Name');
     this_row.find('input[name$=".ForeignId"]').attr('name', 'Model.GenericObjects[' + itemIndex + '].ForeignId');
     this_row.find('input[name$=".Value"]').attr('name', 'Model.GenericObjects[' + itemIndex + '].Value');
     itemIndex++;
   );
 );

基本上变成了这样:

$('#mashup-form').on('click', '.remove-generic', function () 
   $(this).parent().parent().remove();
   var index = 0;
   var itemIndex = 0;
   $('#genericObjects div.row.generic div.col-md-12').each(function () 
     var this_row = $(this);
     this_row.find('input[name$=".Name"]').attr('name', 'GenericObjects[' + itemIndex + '].Name');
     this_row.find('input[name$=".ForeignId"]').attr('name', 'GenericObjects[' + itemIndex + '].ForeignId');
     this_row.find('input[name$=".Value"]').attr('name', 'GenericObjects[' + itemIndex + '].Value');
     itemIndex++;
   );
 );

对于嵌套对象,索引方法与接受的答案(在 EditorFor 系统中)中的方法非常相似,但它们有名称。因此,您将拥有 TESTCs[0].TESTBs1.TESTAs[3] 而不是 [0].1.TESTAs[3](这是用于对象类 TESTC,其中包含 TESTB 类对象,反过来又包含 TESTA 类对象)。

遗憾的是,我可能无法展示我为重置索引而编写的代码(我认为这是一个相当不错的可重用方法),但我可以概述核心概念。首先,只要 .each 指的是在必要时创建的每个 HTML 元素,您只需指定元素的根并使用 .find 即可通过名称查找每个输入并更改其中的必要索引(您不必导航到它们,因为无论如何您都想重置它们)。

至于重置,我已经在使用jquery,所以我只是使用了.attr .replace 方法。替换是通过正则表达式完成的,希望我可以分享一下。

对于第一个层级,使用这个:

(.*?\[)(.*?)\] 

要替换的字符串应该是"$1" + index + "]"

第 2 级只是第一个的复制粘贴:

(.*?\[)(.*?\[)(.*?)\], where (.*?\[) is a single instance of symbols till [

要替换的字符串应该是"$1$2" + index + "]"

只需复制 (.*?[) 所需的任意次数并在替换字符串中相应地添加“$1$2$3...”组标识符即可实现更高级别。这似乎也很普遍(没关系对象名称实际上是)。您当然需要指定所有输入字段名称。有一个好的。希望这可能对某人有所帮助。

编辑 2 后:我在 Stephen Muecke 的评论中注意到处理问题的一种相当不错的方法。在这种特殊情况下,我没有使用它,因为我想尽可能避免修改核心系统。但是,我建议您看看这个解决方案,因为这最终可以让您避免进行任何索引重置。

【问题讨论】:

您需要做的就是为索引器包含一个隐藏输入,它允许您回发非零、非连续的索引项。示例参考this answer 【参考方案1】:

编辑:在底部的 html 中,您会看到作为集合内对象的一部分的每个 html 元素都有一个索引号附加到其名称之前。您只需要确保在提交表单时,集合的所有对象在集合中都有连续的索引,例如如果您有元素 1、2 和 3,并且您删除了元素 2。当您提交其他两个时,html 元素的 name 属性必须包含连续的索引。 Object 1 => "[0].property", Object 2(3 before you remove 2) => "[ 1].property" (由于某种原因,我不能在没有空格的情况下在方括号内写一个,但省略空间。

重要提示:为编辑器模板添加文件夹时,请务必将其添加到共享文件夹中,并且名称为:EditorTemplates(复数)。

您是否尝试过使用模板来显示 GenericObjects 而不是为每个元素渲染一个 partialView?

http://www.growingwiththeweb.com/2012/12/aspnet-mvc-display-and-editor-templates.html

这样您就可以使用 EditorFor 帮助器来处理 GenericObjects 集合的索引。

ASP.NET MVC 4 - EditorTemplate for nested collections

我写了一个例子:

视图模型

public class B

    public string Name  get; set; 
    public string ForeignId  get; set; 
    public int Value  get; set; 

public class A

    public B TestB1  get; set; 
    public B[] TestB2  get; set; 

B 的模板

@model WebApplication1.Models.B

<div class="form-group">
    @Html.TextBoxFor(m => m.Name)
    @Html.TextBoxFor(m => m.ForeignId)
    @Html.TextBoxFor(m => m.Value)
</div>

A 模板

@model WebApplication1.Models.A

<div class="form-group">
    @Html.EditorFor(m => m.TestB1)
    @Html.EditorFor(m => m.TestB2)
</div>

主视图

@model IEnumerable<WebApplication1.Models.A>
@
    Layout = null;


<div class="row">
    <div class="col-xs-12">
        @using (Ajax.BeginForm("Index", new AjaxOptions  HttpMethod = "POST" ))
        
            @Html.EditorFor(m => m)
            <button type="submit">Submit</button>
        
    </div>
</div>

控制器

public class HomeController : Controller
    
        public ActionResult Index()
        
            var model = new List<A>
            
                new A 
                        TestB1 = new B  Name = "a", ForeignId = "a1", Value = 1 ,
                        TestB2 = new B[]
                        
                            new B  Name = "b", ForeignId = "b2", Value = 2,
                            new B  Name = "c", ForeignId = "c3", Value = 3
                        
                     ,
                new A 
                        TestB1 = new B  Name = "aa", ForeignId = "aa1", Value = 1 ,
                        TestB2 = new B[]
                        
                            new B  Name = "bb", ForeignId = "bb2", Value = 2,
                            new B  Name = "cc", ForeignId = "cc3", Value = 3
                        
                     ,
                new A 
                        TestB1 = new B  Name = "aaa", ForeignId = "aaa1", Value = 1 ,
                        TestB2 = new B[]
                        
                            new B  Name = "bbb", ForeignId = "bbb2", Value = 2,
                            new B  Name = "ccc", ForeignId = "ccc3", Value = 3
                        
                     
            ;
            return View(model);
        
        [HttpPost]
        public JsonResult Index(List<A> model)
        
            return Json("a");
        
    

渲染视图

查看标记

<form id="form0" action="/" method="post" data-ajax-method="POST" data-ajax="true"><div class="form-group">
    <div class="form-group">
    <input name="[0].TestB1.Name" type="text" value="a">
    <input name="[0].TestB1.ForeignId" type="text" value="a1">
    <input name="[0].TestB1.Value" type="text" value="1" data-val-required="The Value field is required." data-val-number="The field Value must be a number." data-val="true">
</div>
    <div class="form-group">
    <input name="[0].TestB2[0].Name" type="text" value="b">
    <input name="[0].TestB2[0].ForeignId" type="text" value="b2">
    <input name="[0].TestB2[0].Value" type="text" value="2" data-val-required="The Value field is required." data-val-number="The field Value must be a number." data-val="true">
</div><div class="form-group">
    <input name="[0].TestB2[1].Name" type="text" value="c">
    <input name="[0].TestB2[1].ForeignId" type="text" value="c3">
    <input name="[0].TestB2[1].Value" type="text" value="3" data-val-required="The Value field is required." data-val-number="The field Value must be a number." data-val="true">
</div>
</div><div class="form-group">
    <div class="form-group">
    <input name="[1].TestB1.Name" type="text" value="aa">
    <input name="[1].TestB1.ForeignId" type="text" value="aa1">
    <input name="[1].TestB1.Value" type="text" value="1" data-val-required="The Value field is required." data-val-number="The field Value must be a number." data-val="true">
</div>
    <div class="form-group">
    <input name="[1].TestB2[0].Name" type="text" value="bb">
    <input name="[1].TestB2[0].ForeignId" type="text" value="bb2">
    <input name="[1].TestB2[0].Value" type="text" value="2" data-val-required="The Value field is required." data-val-number="The field Value must be a number." data-val="true">
</div><div class="form-group">
    <input name="[1].TestB2[1].Name" type="text" value="cc">
    <input name="[1].TestB2[1].ForeignId" type="text" value="cc3">
    <input name="[1].TestB2[1].Value" type="text" value="3" data-val-required="The Value field is required." data-val-number="The field Value must be a number." data-val="true">
</div>
</div><div class="form-group">
    <div class="form-group">
    <input name="[2].TestB1.Name" type="text" value="aaa">
    <input name="[2].TestB1.ForeignId" type="text" value="aaa1">
    <input name="[2].TestB1.Value" type="text" value="1" data-val-required="The Value field is required." data-val-number="The field Value must be a number." data-val="true">
</div>
    <div class="form-group">
    <input name="[2].TestB2[0].Name" type="text" value="bbb">
    <input name="[2].TestB2[0].ForeignId" type="text" value="bbb2">
    <input name="[2].TestB2[0].Value" type="text" value="2" data-val-required="The Value field is required." data-val-number="The field Value must be a number." data-val="true">
</div><div class="form-group">
    <input name="[2].TestB2[1].Name" type="text" value="ccc">
    <input name="[2].TestB2[1].ForeignId" type="text" value="ccc3">
    <input name="[2].TestB2[1].Value" type="text" value="3" data-val-required="The Value field is required." data-val-number="The field Value must be a number." data-val="true">
</div>
</div>            <button type="submit">Submit</button>
</form>

【讨论】:

还没试过。我想我明天会调查它,前提是没有出现另一个选项。该系统实际上并不是由我编写的,所以如果可以避免的话,我对进行太多更改感到有点厌倦。话虽如此,也许不可能。此外,这种方法是否适用于对象内的对象。假设我有两个 A 类对象 TESTA1 和 TESTA2,分别包含 B 类的 TESTB1、TESTB2 和 TESTB3、TESTB4。我能编辑第一级和第二级对象(A 类和 B 类)吗? 当然,您为每种类型创建一个模板,我稍后会尝试改进我的答案。 看起来不错,在我最后一次尝试使这个想法奏效(它可能不会)之后,我会尝试这样做。谢了哥们。但是,我不太确定两件事。首先,如何添加对象有没有办法像我以前的系统一样获取索引?此外,删除它们是否相同(使用 $(this).remove();)?另外,据我了解,我之前使用的文章中描述的删除现在应该可以工作了,对吧?最后,这是一个通用的提交按钮,它提交给什么(它如何决定调用什么方法)?干杯,伙计。 通过一些调整它应该可以工作。文章中的代码将新索引重新分配给其余元素。但是您应该注意每个生成的输入的名称属性,因为 Model.GenericObjects 可能不是由 Html 帮助程序生成的,所以这可能就是代码不起作用的原因。您需要确定视图如何命名 html 元素。 对于 ASP.NET MVC 的表单,您正在提交表单内的每个输入,并且框架正在尝试将该集合绑定到模型或正在处理的操作的任何其他参数控制器端的提交请求。

以上是关于如何使用 jquery 从 asp.net 视图模型中动态删除行而不删除集合中的其余下一个对象的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Jquery 将数据从视图发送到控制器 Asp.net MVC

在 ASP.NET MVC 中使用 jQuery 渲染局部视图

如何使用 JQuery c# 在嵌套网格视图 ASP.net 中使用日期时间选择器

如何使用 jquery 或 ajax 在 c#/asp.net 中为 MVC 项目更新 razor 部分视图

如何在 ASP.NET MVC 部分视图中使用 JQuery 表排序器插件?当我在视图中插入 PartialView 时,我使用的是 ajax

ASP.NET MVC 主视图引用的js(jquery)在分部视图中无效,如何解决?