이 작업의 구현에 들어가기 전에 스티븐 샌더슨으로부터 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; }
}
나는이 페이지에 대한 나의 모델은 절대적으로, 목록
찬성해야한다고 가정하자.
그럼 가서 만들 수 있도록 우리의 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
여기에 사용 된 도우미는 Steven Sanderson의 블로그 게시물에서 가져온 것입니다. 이전에 내 답변에 링크되어 있으며 이미 읽고 익숙한 것입니다. 여기에 완전성의 소스 코드는 다음과 같습니다
는
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
태그입니다 것으로 나타났습니다. 면도기의 견해가 당신의 사건에는 적용되지 않는다고 생각합니다. 여전히 다른 모든 것들은 똑같이 작동해야합니다. 여기
~/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>
감사 대린, 이 잘 작동합니다. 좀 더 도움이 필요합니다. 새로운 속성으로 Person 클래스를 확장하고 싶습니다. 'public int Percent {get; 세트; }' IEnumerable에있는 각 퍼센 트의 합계가 100 인 경우 제출시 서버 유효성 검사를 원합니다.이 사용자 정의 특성을 어떻게 만들 수 있습니까? 제 모델은 제네릭 목록입니다. 제발 [CustomAttribute]를 적용 할 수 없습니다. 맞습니까? –
Cemsha
또한 입력의 직후가 아닌 페이지 상단에 검증 요약이 있어야합니다. 나는 넣었다 : '<% : Html.ValidationSummary (false, "다음을 수정하고 페이지를 다시 제출하십시오.") %>' Person마다 각각 다른 검증 메시지를 설정할 방법이 있습니까? 감사. – Cemsha