部分加载的视图上的回发会带来null模型

时间:2013-02-23 11:57:18

标签: asp.net-mvc jquery unobtrusive-ajax

我的应用程序包含搜索框很少的页面。当用户点击相应的搜索按钮时,预计会加载结果网格。该网格具有很少的可编辑控件,用户可以在其中修改并点击保存按钮以保存数据。

我们已使用AjaxForm和部分视图实现了该功能。

  1. 使用Ajax.BeginForm创建包含搜索框和搜索按钮的搜索区域,在提交时从控制器调用回发搜索方法。 SearchModel将传递给此方法。
  2. 创建部分视图以显示结果,步骤1中的Ajax表单从控制器回发方法成功加载。 SearchModel.Results属性作为模型传递给视图。
  3. 这个显示结果的局部视图有一个Save按钮(同样是一个Ajax表单),它在控制器中调用另一个方法,但在控制器中获取模型null。
  4. 已经尝试了很多技巧,但没有成功。任何工作实例都证明了这一点可行吗? Web上有很多例子解释了AjaxForm用于加载数据的用法,但没有找到多个(或嵌套?)

    提前致谢。

    编辑 - 2月24日

    以下是我使用Visual Studio默认MVC模板创建的示例,它与上面解释的实际标准类似,在提交部分页面时也存在同样的问题。

    查看:

    Index.cshtml

        @using MvcApplication1.Models
        @model SearchModel
    
        @{
            ViewBag.Title = "Home Page";
        }
    
         <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" ></script>
        <h2>@ViewBag.Message</h2>
        <p>
            To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
        </p>
    
    
    
    @Ajax.BeginForm("Search", "Home", new AjaxOptions { HttpMethod = "Post", 
    InsertionMode = InsertionMode.Replace,UpdateTargetId = "SearchResults"})
    {
         <table>
        <tr>
            <td>
                First Name:
            </td>
            <td>
                @Html.TextBoxFor(m => m.SearchString)
            </td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="Submit" />
            </td>
        </tr>
    </table>
    
    }
    <div id="SearchResults" style="color: Green;"></div>
    

    部分视图 - _SearchResult.cshtml

        @using MvcApplication1.Models
        @model MvcApplication1.Models.SearchModel
        @{
            ViewBag.Title = "Result Partial";
        }
        <h2>
            testPartial</h2>
        @Ajax.BeginForm("SearchResult", "Home", new AjaxOptions
        {
            HttpMethod = "Post"
        })
        {
        <table>
            @foreach (ResultModel item in Model.Result)
            {
                <tr>
                    <td>
                        Name:
                    </td>
                    <td>
                        @Html.DisplayFor(m => item.Name)
                    </td>
                </tr>
                <tr>
                    <td>
                        Address:
                    </td>
                    <td>
                        @Html.TextAreaFor(m => item.Address)
                    </td>
                </tr>
    
            }
            <tr>
                <td colspan="2">
                    <input type="submit" value="Submit" />
                </td>
            </tr>
        </table>
        }
    

    型号:

    SearchModel.cs

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Web;
    
        namespace MvcApplication1.Models
        {
            public class SearchModel
            {
                public string SearchString  { get; set; }
    
                public List<ResultModel> Result { get; set; }
            }
        }
    

    ResultModel.cs

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Web;
    
        namespace MvcApplication1.Models
        {
            public class ResultModel
            {
                public string Name { get; set; }
                public string Address { get; set; }
            }
        }
    

    控制器:

    HomeController.cs

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Web;
        using System.Web.Mvc;
        using MvcApplication1.Models;
    
        namespace MvcApplication1.Controllers
        {
            public class HomeController : Controller
            {
                public ActionResult Index()
                {
                    ViewBag.Message = "Welcome to ASP.NET MVC!";
    
                    return View();
                }
    
                public ActionResult About()
                {
                    return View();
                }
    
                [HttpPost]
                public ActionResult Search(SearchModel model)
                {
                    //dummy search for result
                    List<ResultModel> result = new List<ResultModel>();
    
                    ResultModel res1 = new ResultModel();
                    res1.Name = model.SearchString + " 1";
                    res1.Address = "Dummy address";
    
                    result.Add(res1);
    
                    ResultModel res2 = new ResultModel();
                    res2.Name = model.SearchString + " 2";
                    res2.Address = "Rummy address";
    
                    result.Add(res2);
    
                    //assign seach results to model
                    model.Result = result;
    
                    return PartialView("_SearchResult", model);
                }
    
                [HttpPost]
                public ActionResult SearchResult(SearchModel model)
                {
                    //do something with results
    
                    List<ResultModel> res = model.Result; // null here !!
    
                    return RedirectToAction("Index");
                }
            }
        }
    

    所以,上面的样本

    1. 在主页上显示搜索框 - Index.cshtml
    2. 当您点击提交时,它会加载部分视图,显示搜索结果
    3. 当您编辑搜索结果并点击结果表单上的提交时,请在那里设置断点,您将看到返回的模型为空。
    4. 希望,这可以解释我的问题。

1 个答案:

答案 0 :(得分:1)

现在您已经用完整的示例展示了您的实际代码,您的问题可以得到解答。

您获得null的原因是因为您不遵守默认模型绑定器所期望的输入字段的标准命名约定。请阅读Phil Haack的following article以熟悉这些惯例。

您的代码存在的问题是您在部分内部使用foreach循环来渲染结果而不是使用编辑器模板。因此,请使用以下内容替换_SearchResult.cshtml中的代码:

@using MvcApplication1.Models
@model SearchModel
@{
    ViewBag.Title = "Result Partial";
}
<h2>testPartial</h2>

@using(Ajax.BeginForm("SearchResult", "Home", new AjaxOptions()))
{
    <table>
        @Html.EditorFor(x => x.Result)
        <tr>
            <td colspan="2">
                <input type="submit" value="Submit" />
            </td>
        </tr>
    </table>
}

然后为ResultModel类型定义自定义编辑器模板(~/Views/Shared/EditorTemplates/ResultModel.cshtml - 警告,编辑器模板的名称和位置很重要,因为它按惯例工作):

@using MvcApplication1.Models
@model ResultModel

<tr>
    <td>
        Name:
    </td>
    <td>
        @Html.DisplayFor(m => m.Name)
        @Html.HiddenFor(m => m.Name)
    </td>
</tr>
<tr>
    <td>
        Address:
    </td>
    <td>
        @Html.TextAreaFor(m => m.Address)
    </td>
</tr>

需要注意的事项:

  • 我已将Ajax.BeginForm帮助器包装在using语句中。您应该在Index.cshtml视图
  • 中执行相同的操作
  • 我在自定义编辑器模板(@Html.HiddenFor(m => m.Name))中为Name属性添加了一个隐藏字段,以便在提交表单时将此值发送到服务器,因为您只有一个TextArea用于Address字段含义将永远不会将名称发送到您的服务器。