2013-01-02 138 views
1

我有我的asp.net mvc 2应用程序有多个页面的注册向导。在第一页上,我应该有参与这个过程的人的基本数据表格。我应该有3个文本框标记为名字,姓氏和地址,以及1个带有“添加其他人”文本的复选框。当用户单击单选按钮时,新的文本框将显示新的单选按钮,因此我们可以使用同一表单添加多个人员。理论上,我们应该能够尽可能多地插入。所有字段都是必填字段,因此在页面顶部的验证摘要中,我应该有类似“请输入第二个人的名字”或类似的内容。我有DTO类:ASP.NET MVC 2动态页面验证

public class Person 
{ 
    public string FullName { get; set; } 
    public string LastName { get; set; } 
    public string Address{ get; set; } 
} 

,我想我的这个页面模型应该是List<Person>,我会追加的HTML与JavaScript/jQuery的新的人。请帮助我,我应该如何验证这个动态页面?我可以通过Save and Back按钮浏览这个向导,并且我们应该能够在页面上取消单击任何单选按钮,并且该特定的人应该消失,验证人不应该再捕捉它。我的整个向导正在使用服务器端验证(DataAnnotations),我不想使用客户端验证。提前致谢。

UPDATE:

我需要一些更多的帮助。我想扩展Person类与新的属性:

public int Percent { get; set; } 

,我想在提交服务器验证,如果每个在IEnumerable<Person>人的所有的百分比之和等于100,我能为这,以及如何创建自定义属性?我的模型是通用列表,我不能在其上应用[CustomAttribute],对不对?
此外,我应该在页面顶部有验证摘要,而不是在每个输入之后。我已经把:<%:Html.ValidationSummary(false, "Please correct the following and resubmit the page:")%>有没有一种方法来为每个人设置不同的验证信息?谢谢

回答

3

在开始执行此任务之前,我会强烈建议您阅读Steven Sanderson的Editing a variable length list, ASP.NET MVC 2-style

准备好了吗?

好的,现在我们可以进入实施。

第一件事是为我们的任务定义我们的视图模型。你已经有了,只是其上定义相应的验证规则:

public class Person 
{ 
    [Required] 
    public string FullName { get; set; } 

    [Required] 
    public string LastName { get; set; } 

    [Required] 
    public string Address{ get; set; } 
} 

,我想我的这个网页模式应该是List

赞成票,绝对。

因此,让我们继续前进,创造我们PersonsController

public class PersonsController : Controller 
{ 
    public ActionResult Index() 
    { 
     var model = new[] 
     { 
      new Person() 
     }; 
     return View(model); 
    } 

    [HttpPost] 
    public ActionResult Index(IEnumerable<Person> persons) 
    { 
     if (!ModelState.IsValid) 
     { 
      return View(persons); 
     } 

     // To do: do whatever you want with the data 
     // In this example I am simply dumping it to the output 
     // but normally here you would update your database or whatever 
     // and redirect to the next step of the wizard 
     return Content(string.Join(Environment.NewLine, persons.Select(p => string.Format("name: {0} address: {1}", p.FullName, p.Address)))); 
    } 

    public ActionResult BlankEditorRow() 
    { 
     return PartialView("_PersonEditorRow", new Person()); 
    } 
} 

现在让我们来定义视图(~/Views/Persons/Index.cshtml):

@model IEnumerable<Person> 

@using (Html.BeginForm()) 
{ 
    <div id="editorRows"> 
     @foreach (var item in Model) 
     { 
      Html.RenderPartial("_PersonEditorRow", item); 
     } 
    </div>  

    @Html.ActionLink(
     "Add another person", 
     "BlankEditorRow", 
     null, 
     new { id = "addItem" } 
    ) 

    <p> 
     <button type="submit">Next step</button> 
    </p> 
} 

<script type="text/javascript"> 
    $('#addItem').click(function() { 
     $.ajax({ 
      url: this.href, 
      cache: false, 
      success: function (html) { $('#editorRows').append(html); } 
     }); 
     return false; 
    }); 

    $(document).delegate('a.deleteRow', 'click', function() { 
     $(this).parents('div.editorRow:first').remove(); 
     return false; 
    }); 
</script> 

和相应的局部视图(~/Views/Persons/_PersonEditorRow.cshtml):

@model Person 

<div class="editorRow"> 
    @using(Html.BeginCollectionItem("persons")) 
    { 
     <div> 
      @Html.LabelFor(x => x.FullName) 
      @Html.EditorFor(x => x.FullName) 
      @Html.ValidationMessageFor(x => x.FullName) 
     </div> 
     <div> 
      @Html.LabelFor(x => x.LastName) 
      @Html.EditorFor(x => x.LastName) 
      @Html.ValidationMessageFor(x => x.LastName) 
     </div> 
     <div> 
      @Html.LabelFor(x => x.Address) 
      @Html.EditorFor(x => x.Address) 
      @Html.ValidationMessageFor(x => x.Address) 
     </div> 

     <a href="#" class="deleteRow">delete</a> 
    } 
</div> 

备注:Html.BeginCollectionItem这里使用的助手是从史蒂文桑德森的博客文章,我已经链接到以前在我的答案,你已经阅读和熟悉。下面是完整的源代码:

public static class HtmlPrefixScopeExtensions 
{ 
    private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_"; 

    public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName) 
    { 
     var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName); 
     string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString(); 

     // autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync. 
     html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex))); 

     return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex)); 
    } 

    public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix) 
    { 
     return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix); 
    } 

    private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName) 
    { 
     // We need to use the same sequence of IDs following a server-side validation failure, 
     // otherwise the framework won't render the validation error messages next to each item. 
     string key = idsToReuseKey + collectionName; 
     var queue = (Queue<string>)httpContext.Items[key]; 
     if (queue == null) 
     { 
      httpContext.Items[key] = queue = new Queue<string>(); 
      var previouslyUsedIds = httpContext.Request[collectionName + ".index"]; 
      if (!string.IsNullOrEmpty(previouslyUsedIds)) 
       foreach (string previouslyUsedId in previouslyUsedIds.Split(',')) 
        queue.Enqueue(previouslyUsedId); 
     } 
     return queue; 
    } 

    private class HtmlFieldPrefixScope : IDisposable 
    { 
     private readonly TemplateInfo templateInfo; 
     private readonly string previousHtmlFieldPrefix; 

     public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix) 
     { 
      this.templateInfo = templateInfo; 

      previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix; 
      templateInfo.HtmlFieldPrefix = htmlFieldPrefix; 
     } 

     public void Dispose() 
     { 
      templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix; 
     } 
    } 
} 

UPDATE:

我不好,我只注意到你的问题被标记为asp.net-mvc-2。所以我想我的剃刀视图不适用于你的案例。不过,其他一切都应该一样。所有你需要做的是更新的意见,使他们使用的WebForms视图引擎:

这里的~/Views/Persons/Index.aspx

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<IEnumerable<Person>>" %> 
<% using (Html.BeginForm()) { %> 
    <div id="editorRows"> 
     <% foreach (var item in Model) { %> 
      <% Html.RenderPartial("_PersonEditorRow", item); %> 
     <% } %> 
    </div>  

    <%= Html.ActionLink(
     "Add another person", 
     "BlankEditorRow", 
     null, 
     new { id = "addItem" } 
    ) %> 

    <p> 
     <button type="submit">Next step</button> 
    </p> 
<% } %> 

<script type="text/javascript"> 
    $('#addItem').click(function() { 
     $.ajax({ 
      url: this.href, 
      cache: false, 
      success: function (html) { $('#editorRows').append(html); } 
     }); 
     return false; 
    }); 

    $(document).delegate('a.deleteRow', 'click', function() { 
     $(this).parents('div.editorRow:first').remove(); 
     return false; 
    }); 
</script> 

,最后(~/Views/Persons/_PersonEditorRow.ascx)部分:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Person>" %> 
<div class="editorRow"> 
    <% using(Html.BeginCollectionItem("persons")) { %> 
     <div> 
      <%= Html.LabelFor(x => x.FullName) %> 
      <%= Html.EditorFor(x => x.FullName) %> 
      <%= Html.ValidationMessageFor(x => x.FullName) %> 
     </div> 
     <div> 
      <%= Html.LabelFor(x => x.LastName) %> 
      <%= Html.EditorFor(x => x.LastName) %> 
      <%= Html.ValidationMessageFor(x => x.LastName) %> 
     </div> 
     <div> 
      <%= Html.LabelFor(x => x.Address) %> 
      <%= Html.EditorFor(x => x.Address) %> 
      <%= Html.ValidationMessageFor(x => x.Address) %> 
     </div> 

     <a href="#" class="deleteRow">delete</a> 
    <% } %> 
</div> 
+0

感谢达林,这工作正常。 我需要一些更多的帮助。我想用新的属性扩展Person类: 'public int Percent {get;组; }' 我希望在提交时验证服务器验证,如果IEnumerable 中的每个人中的所有百分数的总和等于100.我可以为此创建自定义属性以及如何?我的模型是泛型列表,我无法应用[CustomAttribute],对吧? – Cemsha

+0

另外,我应该在页面顶部有验证摘要,而不是在每个输入之后。我已经提出: '<%:Html.ValidationSummary(false,“请更正以下内容并重新提交页面:”)%>' 是否有方法为每个Person设置不同的验证消息?谢谢。 – Cemsha