【问题标题】:How to Update List<Model> with jQuery in MVC 4如何在 MVC 4 中使用 jQuery 更新 List<Model>
【发布时间】:2013-03-04 16:02:32
【问题描述】:

我目前正在尝试使用修改后的索引视图创建设置页面。目标是让用户显示所有设置,并且可以在一个视图中更改所有设置使用一个按钮保存所有设置。应使用 Ajax 更新设置。

我目前的做法:

查看:

<script language="javascript">
    $(function() {
        $('#editSettings').submit(function () {
            if ($(this).valid()) {
                $.ajax({
                    url: this.action,
                    type: this.method,
                    data: $(this).serialize(),
                    success: function (result)
                    {
                        alert(result);                       
                    }
                });
            }
            return false;
        });
    });
</script>

[ ... ]

@using (Ajax.BeginForm("Edit", "Settings", new AjaxOptions {UpdateTargetId = "result"}, new { @class = "form-horizontal", @id = "editSettings" } ))
{
    foreach (Setting item in ViewBag.Settings) 
    {
        @Html.Partial("_SingleSetting", item)
    }
    <input type="submit" value="modify" />
}

部分视图加载设置:

        <div class="control-group">
            <label class="control-label">@settingName</label>
            <div class="controls">
                @Html.EditorFor(model => model.Value)
                <span class="help-inline">@settingDescription</span>
            </div>
        </div>

型号:

[Table("Settings")]
public class Setting
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int SettingId { get; set; }

    public string Name { get; set; }

    [Required(AllowEmptyStrings = true)]
    [DisplayFormat(ConvertEmptyStringToNull = false)]
    public string Value { get; set; }
}

我正在使用 ViewBag.Settings = _db.Settings.ToList(); 设置 ViewBag

jQuery 将 Data 解析为以下方法:

    [HttpPost]
    public ActionResult Edit(IList<Setting> setting)
    {
        Console.WriteLine(setting.Count);
        return Content(""); // Currently for testing purposes only. Breakpoint is set to setting.Count
    }

Count 抛出错误,因为设置为 null。我很不确定如何解决这个问题。

谁能给我一个提示?

This 关于 SO 的主题已经涵盖了在没有 Ajax 的情况下更新集合。但我不会明白这一点。

感谢您的帮助。

【问题讨论】:

    标签: jquery collections asp.net-mvc-4


    【解决方案1】:

    您正在使用Ajax.BeginForm 并再次使用 jQuery 对表单进行 ajaxifying。那没有必要。但是您的代码的真正问题是部分输入字段的名称。您不尊重默认模型绑定器用于绑定到列表的naming convention

    让我们举一个完整的例子(为简单起见,去除了实体框架等所有噪音):

    型号:

    public class Setting
    {
        public int SettingId { get; set; }
        public string Name { get; set; }
        public string Value { get; set; }
    }
    

    控制器:

    public class SettingsController : Controller
    {
        public ActionResult Index()
        {
            // No idea why you are using ViewBag instead of view model
            // but I am really sick of repeating this so will leave it just that way
            ViewBag.Settings = Enumerable.Range(1, 5).Select(x => new Setting
            {
                SettingId = x,
                Name = "setting " + x,
                Value = "value " + x
            }).ToList();
            return View();
        }
    
        [HttpPost]
        public ActionResult Edit(IList<Setting> setting)
        {
            // Currently for testing purposes only. Breakpoint is set to setting.Count
            return Content(setting.Count.ToString()); 
        }
    }
    

    查看(~/Views/Settings/Index.cshtml):

    @using (Html.BeginForm("Edit", "Settings", FormMethod.Post, new { @class = "form-horizontal", id = "editSettings" }))
    {
        foreach (Setting item in ViewBag.Settings) 
        {
            @Html.Partial("_SingleSetting", item)
        }
        <input type="submit" value="modify" />
    }
    
    @section scripts {
        <script type="text/javascript">
            $('#editSettings').submit(function () {
                if ($(this).valid()) {
                    $.ajax({
                        url: this.action,
                        type: this.method,
                        data: $(this).serialize(),
                        success: function (result) {
                            alert(result);
                        }
                    });
                }
                return false;
            });
        </script>
    }
    

    部分设置 (~/Views/Settings/_SingleSetting.cshtml):

    @model Setting
    @{
        var index = Guid.NewGuid().ToString();
        ViewData.TemplateInfo.HtmlFieldPrefix = "[" + index + "]";
    }
    
    <input type="hidden" name="index" value="@index" />
    
    <div class="control-group">
        <label class="control-label">@Html.LabelFor(x => x.Name)</label>
        <div class="controls">
            @Html.EditorFor(model => model.Value)
        </div>
    </div>
    

    注意在部分内部,有必要更改 HtmlFieldPrefix 以便 html 帮助程序为您的输入字段生成正确的名称并遵守命名约定。


    好的,现在让我们删除 ViewCrap 并正确地做事(即当然是使用视图模型)。

    和往常一样,我们从编写视图模型开始:

    public class MyViewModel
    {
        public IList<Setting> Settings { get; set; }
    }
    

    然后我们适配控制器:

    public class SettingsController : Controller
    {
        public ActionResult Index()
        {
            var model = new MyViewModel();
    
            // you will probably wanna call your database here to 
            // retrieve those values, but for the purpose of my example that
            // should be fine
            model.Settings = Enumerable.Range(1, 5).Select(x => new Setting
            {
                SettingId = x,
                Name = "setting " + x,
                Value = "value " + x
            }).ToList();
            return View(model);
        }
    
        [HttpPost]
        public ActionResult Edit(IList<Setting> setting)
        {
            // Currently for testing purposes only. Breakpoint is set to setting.Count
            return Content(setting.Count.ToString()); 
        }
    }
    

    查看(~/Views/Settings/Index.cshtml):

    @model MyViewModel
    
    @using (Html.BeginForm("Edit", "Settings", FormMethod.Post, new { @class = "form-horizontal", id = "editSettings" }))
    {
        @Html.EditorFor(x => x.Settings)
        <input type="submit" value="modify" />
    }
    
    @section scripts {
        <script type="text/javascript">
            $('#editSettings').submit(function () {
                if ($(this).valid()) {
                    $.ajax({
                        url: this.action,
                        type: this.method,
                        data: $(this).serialize(),
                        success: function (result) {
                            alert(result);
                        }
                    });
                }
                return false;
            });
        </script>
    }
    

    设置模型的编辑器模板 (~/Views/Settings/EditorTemplates/Settings.cshtml):

    @model Setting
    <div class="control-group">
        <label class="control-label">@Html.LabelFor(x => x.Name)</label>
        <div class="controls">
            @Html.EditorFor(model => model.Value)
        </div>
    </div>
    

    现在所有的作品都按照惯例。无需编写任何 foreach 循环。索引视图中的@Html.EditorFor(x =&gt; x.Settings) 调用分析视图模型的设置属性并检测到它是某个其他模型的集合(在本例中为Setting)。因此它将开始循环遍历该集合并搜索相应的编辑器模板 (~/Views/Settings/EditorTemplates/Setting.cshtml),该模板将自动为该集合的每个元素呈现。因此,您甚至不需要在视图中编写任何循环。除了简化代码之外,现在编辑器模板中的Html.EditorFor(x =&gt; x.Value) 将为输入字段生成正确的名称。

    【讨论】:

    • 非常感谢您的详细解答。这给了我一个关于 EditorTemplates 和 List 与 Views 结合使用的全新的View
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-26
    • 2016-10-27
    相关资源
    最近更新 更多