2013-01-02 93 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