【问题标题】:ASP.NET MVC AJAX Update select list items when model is added to separate table将模型添加到单独的表时,ASP.NET MVC AJAX 更新选择列表项
【发布时间】:2017-10-09 03:19:06
【问题描述】:

目前情况: 对于这个问题的本质,我有两个数据库表/模型。当创建和添加新模型时,我正在使用 AJAX 表单从每个表中更新以列表形式显示的模型数据。实际上,页面无需重新加载即可显示对列表的任何更改。

我在项目列表上方静态显示每个模型的创建表单。这适用于没有外键依赖的模型。但是,我的一个模型在创建表单中也有一个选择列表,该列表与页面上的另一个模型列表相关联。

问题: 当我为更简单的模型添加记录时,具有外键依赖关系的模型的创建表单的选择列表不会更新以包含更改。我知道它为什么不更新,我认为我需要使用 AJAX 来完全就地重新创建创建表单或只更新选择列表。

有没有办法使用 AJAX 表单不仅更新我要添加项目的模型列表,而且同时更新包含创建表单的 div?

我相信我已经包含了所有相关代码,希望能更清楚地了解我的要求

为更简单的模型创建表单示例:

@using (Ajax.BeginForm("_CreateCategory", new AjaxOptions()
{
    InsertionMode = InsertionMode.Replace,
    UpdateTargetId = "list-categories"
}))
{
    @Html.AntiForgeryToken()
    <div class="form-horizontal">
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            <label class="control-label col-md-2">Name: </label>
            <div class="col-md-5">
                @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
            </div>
            <div class="col-md-5">
                <input type="submit" value="Add" class="btn btn-success" />
            </div>
        </div>
    </div>
}

具有外键依赖的示例模型

@using (Ajax.BeginForm("_CreateType", new AjaxOptions()
{
    InsertionMode = InsertionMode.Replace,
    UpdateTargetId = "list-types"
}))
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            <label class="control-label col-md-2">Name: </label>
            <div class="col-md-offset-1 col-md-6">
                @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
            </div>
        </div>
        <div class="form-group">
            <label class="control-label col-md-3">Category: </label>
            <div class="col-md-6">
                @Html.DropDownList("CategoryID", null, htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.CategoryID, "", new { @class = "text-danger" })
            </div>
            <div class="col-md-3">
                <input type="submit" value="Add" class="btn btn-success" />
            </div>
        </div>
    </div>
}

带有外键的模型的示例列表部分视图

    <div id="list-types">
        <table class="table">
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.Name)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Category.Name)
                </th>

                <th></th>
            </tr>

        @foreach (var item in Model) {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Category.Name)
                </td>

                <td>
                    @using (Ajax.BeginForm("DeleteType", new { id = item.ID }, new AjaxOptions()
                    {
                        InsertionMode = InsertionMode.Replace,
                        UpdateTargetId = "list-types"
                    }))
                    {
                        @Html.AntiForgeryToken()
                        <button type="submit" class="no-default">
                            <span class="glyphicon glyphicon-remove"></span>
                        </button>
                    }
                </td>
            </tr>
        }

        </table>
    </div>

用于添加更简单模型的控制器操作

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult _CreateCategory([Bind(Include = "ID, Name")] Category category)
    {
        if (ModelState.IsValid)
        {
            db.Categories.Add(category);
            db.SaveChanges();
        }
        var categories = db.Categories;
        return PartialView("_ListCategories", categories.ToList());
    }

显示列表和创建表单的示例索引页面代码

<div class="col-lg-4 block">
    <h3>Categories</h3>
    @Html.Action("_CreateCategory")
    @Html.Action("_ListCategories")
</div>
<div class="col-lg-4 block">
    <h3>Types</h3>
    @Html.Action("_CreateType")
    @Html.Action("_ListTypes")
</div>

【问题讨论】:

  • 从性能的角度来看,您会发现这更好,因为您只需使用 $.ajax() 方法,当您保存 Category 时,只需返回一个 JsonResult 表示成功(或其他),在成功回调中,向 DOM 添加一个新的表格行,并根据表单中的值向您的 &lt;select&gt; 添加一个新的 &lt;option&gt; 元素 - 无需将整个表格作为部分发送回再次查看
  • 你会建议我保持 ajax 表单的使用,而不是使用标准的提交按钮吗?我知道我可以创建一个表单并将其传递给 $.ajax 调用,但我不想不必要地重写我的视图
  • 如果你不想解决问题,那么就让它保持原样:) (除了用&lt;form&gt; 替换@Html.AjaxForm(..) 并编写脚本之外,还有什么要重写的跨度>
  • 我认为你误解了大声笑。迁移到 javascript ajax 没有问题。从我收集到的信息来看,所有的帖子数据都应该以表格的形式提交。将表单本身保留为 Ajax.beginform 是否有任何问题,给它一个 id,或者我最终将它传递给 js ajax 方法,并且默认情况下不使用与 ajax 表单关联的提交操作。还是在这种情况下转换回 html.beginform 格式然后将其传递给 js ajax 调用会更好?
  • 您不需要@Ajax.BeginForm() - 只需一个&lt;form id="..."&gt; 标签即可使用$.ajax()。是的,您应该有一个提交按钮(但您取消了它的操作),因此您可以使用 if('#yourFormId'').valid() 测试客户端验证(并且您不需要任何 data- 属性

标签: c# jquery asp.net ajax asp.net-mvc


【解决方案1】:

您将无法使用Ajax.BeginForm() 方法轻松地做到这一点,而且无论如何,当您只向其中添加一行时再次返回整个表是不必要的额外开销。

仅将Ajax.BeginForm() 替换为&lt;form id="createcategory"&gt; 并使用$.ajax() 提交表单以创建新的Category,并返回其ID。在成功回调中,您可以在表格中添加一个新行,并在第二种形式的&lt;select&gt; 中添加一个新的&lt;option&gt; 元素

var url = '@Url.Action("_CreateCategory")'
var categorySelect = $('#CategoryID');
$('#createcategory').submit(function() {
    if (!$(this).valid()) {
        return; // exit and display validation errors
    }
    var category = $(this).find('input[name="Name"]').val(); // see notes below
    var formdata = $(this).serialize();
    $.post(url, formdata, function(result) {
        if (result.success) {
            // Add option
            categorySelect.append($('<option></option>').val(result.id).text(category));
            // Add new table row
            .... // You have not shown the view for _ListCategories but it might be somethng like
            var cell = $('<td></td>').text(category);
            var row = $('<tr></tr>').append(cell);
            $('#yourTableID').append(row);
        } else {
            .... // check for errors, update the associated placeholder generated by ValidationMessageFor()
        }
    }).fail(function (result) {
        // Oops something went wrong
    });
}

$('#createtype').submit(function() {
    // similar to above, but just add new table row based on the values of the form controls
});

控制器方法是

[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult _CreateCategory(Category category)
{
    if (ModelState.IsValid)
    {
        db.Categories.Add(category);
        db.SaveChanges();
        // Get the new ID
        int id = category.ID;
        return Json(new { success = true, id = id });
    }
    // Get the validation errors
    var errors = ModelState.Keys.Where(k => ModelState[k].Errors.Count > 0).Select(k => new { propertyName = k, errorMessage = ModelState[k].Errors[0].ErrorMessage });
    return Json(new { success = false, errors = errors });
});

一些旁注:

  1. 您的视图包括Name 的 2 个表单控件,因此您的生成 重复的 id 属性是无效的 html。考虑删除 id 属性(使用 new { id = "" } 或给其中一个 不同的id 属性。
  2. 您不需要[Bind] 属性,而且无论如何您不需要 发送ID 的值,因此如果您保留,则应将其排除在外 它。
  3. 您正在编辑数据,因此您应该使用视图模型,并且在 对于第二个模型,它将包含属性 int SelectedCategoryIEnumerable&lt;SelectListItem&gt; CategoryList 和 在视图中使用DropDownListFor(m => m.SelectedCategory, Model.CategoryList, "Please select", new { ... })
  4. 使用@Html.LabelFor(m =&gt; m.yourProperty) 正确生成您的 &lt;label&gt; 元素(&lt;label&gt; 是可访问性元素,并且 单击它会将焦点设置到关联的控件,该控件不会 发生在您当前的代码中)

【讨论】:

  • 非常感谢您的详细解释。那里有很多我以前没有意识到的。关于您的笔记: 1. 很多此代码是由 VS 搭建的。我会检查一下,看看我是否能弄清楚你在说哪些控件。 2. 为什么不呢?这就是 VS 搭建创建和编辑方法的方式。我一直认为这是一个安全的事情。这种情况是否不同,因为它是 Ajax?好吧,我将放弃 ID 绑定。仅供参考,非常感谢您的解释。如果这带有负面语气,那是无意的。
  • 1.你有 2 x EditorFor(m =&gt; m.Name) - 这会生成 &lt;input type="text" id="Name" name="Name" .... /&gt; 这意味着你有重复的 id 属性(这是无效的 html)
  • 2.默认情况下,所有属性都是绑定的,因此使用[Bind(Include = "ID, Name")] 与完全排除属性完全相同。但是无论如何,您只发回 Name 的值 - 您不想要 ID 值,因为它会在您保存 Category 时生成,所以至少它应该只是 [Bind(Include = "Name")] 来防止试图发布ID 的恶意用户(无论是普通提交还是ajax 提交都没有区别)。
  • 话虽如此,最好的方法是始终使用视图模型,这样您就可以防止发布不足和过度发布攻击(以及它带来的所有其他好处)。 What is ViewModel in MVC?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-11
  • 2015-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-25
相关资源
最近更新 更多