2012-06-29 2 views
2

다음 코드는 많이 삭제되었지만 근본적으로 내가 찾고있는 것은 다음과 같습니다 :MVC HTML 편집기 템플릿을 사용하여 비 순차 접두사 컬렉션 색인을 생성하는 방법은 무엇입니까?

동적으로 추가 할 수있는 동안 질문과 해당 답변 선택을 편집하고 싶습니다./페이지에서 질문/대답 선택을 제거하십시오. 이상적으로, 내 항목에 대한 HtmlFieldPrefix는 비 순차적이지만 Html.EditorFor()는 순차 색인을 사용합니다.

내가 답변 선택의는 IEnumerable을 포함하는 질문 뷰 모델이 있습니다

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.QuestionViewModel>" %> 

<%=Html.HiddenFor(m => m.QuestionId)%> 
<%=Html.EditorFor(m => m.AnswerChoices) %> 

그리고 도움말 선택 : 내 질문 부분보기 (Question.ascx)에서

public class QuestionViewModel 
{ 
    public int QuestionId { get; set; } 
    public IEnumerable<AnswerChoiceViewModel> AnswerChoices { get; set; } 
} 

을,이이 편집기 템플릿 (AnswerChoiceViewModel.ascx) :

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.AnswerChoiceViewModel>" %> 

<%=Html.HiddenFor(m => m.AnswerChoiceId)%> 
<%=Html.TextBoxFor(m => m.Name)%> 

Question.ascx 같은 모양의 출력은 다음과 같습니다 이미 쓴

<input type="hidden" id="QuestionId" value="1" /> 
<input type="hidden" id="Question.AnswerChoices[e1424d5e-5585-413c-a1b0-595f39747876].AnswerChoiceId" value="1" /> 
<input type="hidden" id="Question.AnswerChoices[e1424d5e-5585-413c-a1b0-595f39747876].Name" value="Answer Choice 1" /> 

<input type="hidden" id="QuestionId" value="2" /> 
<input type="hidden" id="Question.AnswerChoices[633db1c3-f1e6-470b-9c7f-c138f2d9fa71].AnswerChoiceId" value="2" /> 
<input type="hidden" id="Question.AnswerChoices[633db1c3-f1e6-470b-9c7f-c138f2d9fa71].Name" value="Answer Choice 2" /> 

:

<input type="hidden" id="QuestionId" value="1" /> 
<input type="hidden" id="Question.AnswerChoices[0].AnswerChoiceId" value="1" /> 
<input type="hidden" id="Question.AnswerChoices[0].Name" value="Answer Choice 1" /> 

<input type="hidden" id="QuestionId" value="2" /> 
<input type="hidden" id="Question.AnswerChoices[1].AnswerChoiceId" value="2" /> 
<input type="hidden" id="Question.AnswerChoices[1].Name" value="Answer Choice 2" /> 

는 내가 알고 싶은 것은 페이지는 다음과 같이 렌더링 할 수 있도록 내가 EditorFor에게 사용자 정의 GUID 인덱스를 제공 할 수있는 방법입니다 현재 컨텍스트의 접두사 인덱스를 가져 와서 숨겨진 ".Index"필드에 저장하여 비 순차 인덱스가 올바르게 바인딩 될 수 있도록 도우미 메서드입니다. EditorFor가 인덱스를 어떻게 할당하여 내가 그것을 오버라이드 할 수 있는지 알고 싶을 뿐이다.

답변

0

Html.EditorFor은 아무런 의미가 없으며 input을 모든 적절한 속성으로 렌더링하는 Html 도우미 메서드입니다.

나를 떠올리는 유일한 해결책은 자기 자신을 쓰는 것입니다. 그것은 꽤 간단해야합니다 - 5-10 줄 링. 이 Creating Custom Html Helpers Mvc을 살펴보십시오.

0

스티브 샌더슨은 당신이 찾고있는 것을 할 수있는 simple implementation을 제공했습니다. 나는 최근에 그것을 직접 사용하기 시작했다. 완벽하지는 않지만 작동합니다. 불운하게 그의 BeginCollectionItem 방법을 사용하려면 약간의 마술을해야합니다. 나는 나 자신을 해결하려고 노력하고있다.

2

전에이 문제를 해결하고 유사한 문제를 설명하고 해결 한 S. Sanderson (Knockoutjs의 제작자)의 게시물을 보았습니다. 코드의 일부를 사용하고 필요에 맞게 수정하려고했습니다. 아래 코드를 일부 클래스 (exapmle : Helpers.cs)에 넣고 web.config에 네임 스페이스를 추가합니다.

#region CollectionItem helper 
    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, 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; 
     } 
    } 

    #endregion 

@using (Html.BeginCollectionItem("AnswerChoices")) 
{ 
@Html.HiddenFor(m => m.AnswerChoiceId) 
@Html.TextBoxFor(m => m.Name) 
} 

같은 EditorTemplate 또는 일부를 가지고 목록 렌더링 템플릿 (부분)를 통해 열거 할 수 있습니다 후.

0

또 다른 옵션은 다음과 같이 id 속성을 무시하는 것입니다

@Html.TextBoxFor(m => m.Name, new { id = @guid })

1

그것은이 알아낼 방법이 더 이상 정상적으로보다 더 걸렸다.모두가이 일을하기에 너무 열심히 노력하고 있습니다.

 @{ 
 
      var index = Guid.NewGuid(); 
 
      var prefix = Regex.Match(ViewData.TemplateInfo.HtmlFieldPrefix, @"^(.+)\[\d+\]$").Groups[1].Captures[0].Value; 
 
      //TODO add a ton of error checking and pull this out into a reusable class!!!! 
 
      ViewData.TemplateInfo.HtmlFieldPrefix = prefix + "[" + index + "]"; 
 
     } 
 
     <input type="hidden" name="@(prefix).Index" value="@index"/>
이제

, 이것은 무엇을하고있다 : 비밀 소스 코드의 네 가지 라인입니까? 우리는 새로운 GUID를 얻습니다. 이것은 자동으로 할당 된 정수를 대체하는 새로운 색인입니다. 다음으로 기본 필드 접두어를 얻고 우리가 원하지 않는 int 인덱스를 제거합니다. 기술 채무를 작성했다는 것을 인정한 후에, 모든 editorfor 호출이이를 새로운 접두어로 사용하도록 viewdata를 업데이트합니다. 마지막으로 모델 바인더에 다시 게시 된 입력을 추가하여 이러한 필드를 함께 바인딩하는 데 사용해야하는 인덱스를 지정합니다.

이 마법은 어디에서 발생해야합니까? /Views/Shared/EditorTemplates/Phone.cshtml

@using TestMVC.Models 
 
@using System.Text.RegularExpressions 
 
@model Phone 
 
    <div class="form-horizontal"> 
 
     <hr /> 
 
     @{ 
 
      var index = Guid.NewGuid(); 
 
      var prefix = Regex.Match(ViewData.TemplateInfo.HtmlFieldPrefix, @"^(.+)\[\d+\]$").Groups[1].Captures[0].Value; 
 
      //TODO add a ton of error checking and pull this out into a reusable class!!!! 
 
      ViewData.TemplateInfo.HtmlFieldPrefix = prefix + "[" + index + "]"; 
 
     } 
 
     <input type="hidden" name="@(prefix).Index" value="@index"/> 
 
     <div class="form-group"> 
 
      @Html.LabelFor(model => model.Number, htmlAttributes: new { @class = "control-label col-md-2" }) 
 
      <div class="col-md-10"> 
 
       @Html.EditorFor(model => model.Number, new { htmlAttributes = new { @class = "form-control" } }) 
 
       @Html.ValidationMessageFor(model => model.Number, "", new { @class = "text-danger" }) 
 
      </div> 
 
     </div> 
 

 
     <div class="form-group"> 
 
      @Html.LabelFor(model => model.IsEnabled, htmlAttributes: new { @class = "control-label col-md-2" }) 
 
      <div class="col-md-10"> 
 
       <div class="checkbox"> 
 
        @Html.EditorFor(model => model.IsEnabled) 
 
        @Html.ValidationMessageFor(model => model.IsEnabled, "", new { @class = "text-danger" }) 
 
       </div> 
 
      </div> 
 
     </div> 
 

 
     <div class="form-group"> 
 
      @Html.LabelFor(model => model.Details, htmlAttributes: new { @class = "control-label col-md-2" }) 
 
      <div class="col-md-10"> 
 
       @Html.TextAreaFor(model => model.Details, new { htmlAttributes = new { @class = "form-control" } }) 
 
       @Html.ValidationMessageFor(model => model.Details, "", new { @class = "text-danger" }) 
 
      </div> 
 
     </div> 
 
    </div>

EditorTemplate : 편집기 템플릿 내부? 뭐?! 방법?! 그냥 파일 이름에 대한 개체 이름을 사용하여 위에서 언급 한 디렉토리에 넣어. MVC 대회가 마술을 부리도록하십시오. 기본보기에서 단지는 IEnumerable 속성 편집기를 추가

<div class="form-group"> 
 
@Html.LabelFor(model => model.Phones, htmlAttributes: new { @class = "control-label col-md-2" }) 
 
<div class="col-md-10"> 
 
    @Html.EditorFor(model => model.Phones, new { htmlAttributes = new { @class = "form-control" } }) 
 
</div> 
 
</div>
이제

, 다시 컨트롤러, 할 당신이 (바인딩은 전화를 포함한다) 그는 IEnumerable을 허용하도록 메서드 서명을 업데이트해야합니다 :

 [HttpPost] 
    [ValidateAntiForgeryToken] 
    public ActionResult Create([Bind(Include = "ContactId,FirstName,LastName,Phones")] Contact contact) 
    { 
     if (ModelState.IsValid) 
     { 

      db.Contacts.Add(contact); 
      db.SaveChanges(); 
      //TODO need to update this to save phone numbers 
      return RedirectToAction("Index"); 
     } 

     return View(contact); 
    } 

어떻게 추가하고 페이지에 그들을 제거 할 수 있습니까? 일부 버튼을 추가하고 JavaScript를 바인드하고 해당 모델에 대한보기를 리턴 할 메소드를 컨트롤러에 추가하십시오. Ajax를 다시 잡아 페이지에 삽입하십시오. 이 부분에서 바쁜 일이기 때문에 세부 사항을 해결하도록하겠습니다.