[asp.net-mvc] ASP.NET MVC Html.DropDownList SelectedValue

나는 이것이 RC1이라고 시도한 다음 문제를 해결하지 못한 RC2로 업그레이드했습니다.

// in my controller
ViewData["UserId"] = new SelectList(
    users,
    "UserId",
    "DisplayName",
    selectedUserId.Value); // this has a value

결과 : SelectedValue 속성이 개체에 설정됩니다.

// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["UserId"])%>

결과 : 예상되는 모든 옵션이 클라이언트에 렌더링되지만 선택한 속성이 설정되지 않았습니다. SelectedValue의 항목은 목록 내에 있지만 목록의 첫 번째 항목은 항상 기본적으로 선택됩니다.

어떻게해야합니까?

업데이트
John Feminella의 답변 덕분에 문제가 무엇인지 알아 냈습니다. “UserId”는 내 뷰가 강력하게 입력되는 모델의 속성입니다. Html.DropDownList ( “UserId”가 “UserId”가 아닌 다른 이름으로 변경되면 선택한 값이 올바르게 렌더링됩니다.

이로 인해 값이 모델에 바인딩되지 않습니다.



답변

이 문제를 해결 한 방법입니다.

나는 다음을 가졌다 :

제어 장치:

ViewData["DealerTypes"] = Helper.SetSelectedValue(listOfValues, selectedValue) ;

전망

<%=Html.DropDownList("DealerTypes", ViewData["DealerTypes"] as SelectList)%>

다음에 의해 변경 :

전망

<%=Html.DropDownList("DealerTypesDD", ViewData["DealerTypes"] as SelectList)%>

DropDown은 ViewData 이름이 같은 이름을 가져서는 안되는 것 같습니다 : S 이상하지만 작동했습니다.


답변

이 시도:

public class Person {
    public int Id { get; set; }
    public string Name { get; set; }
}

그리고:

var list = new[] {
    new Person { Id = 1, Name = "Name1" },
    new Person { Id = 2, Name = "Name2" },
    new Person { Id = 3, Name = "Name3" }
};

var selectList = new SelectList(list, "Id", "Name", 2);
ViewData["People"] = selectList;

Html.DropDownList("PeopleClass", (SelectList)ViewData["People"])

MVC RC2를 사용하면 다음을 얻을 수 있습니다.

<select id="PeopleClass" name="PeopleClass">
    <option value="1">Name1</option>
    <option selected="selected" value="2">Name2</option>
    <option value="3">Name3</option>
</select>


답변

DropDown의 이름을 “UserId”로 지정하고 모델 바인딩이 올바르게 작동하도록 할 수 있습니다.

이 작업을 수행하기위한 유일한 요구 사항은 SelectList를 포함하는 ViewData 키가 바인딩하려는 Model 속성과 이름이 같지 않다는 것입니다. 특정 경우에는 다음과 같습니다.

// in my controller
ViewData["Users"] = new SelectList(
    users,
    "UserId",
    "DisplayName",
    selectedUserId.Value); // this has a value

// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["Users"])%>

그러면 모델의 UserId 속성과 이름이 같은 UserId라는 선택 요소가 생성되므로 모델 바인더는 Html.DropDownList 도우미가 생성 한 html의 선택 요소에서 선택한 값으로이를 설정합니다.

속성 이름과 동일한 키를 사용하여 ViewData에 선택 목록을 넣을 때 특정 Html.DropDownList 생성자가 SelectList에 지정된 값을 선택하지 않는 이유를 잘 모르겠습니다. 다른 시나리오에서 DropDownList 도우미가 사용되는 방식과 관련이 있다고 생각합니다. 관례는 모델의 속성과 동일한 이름을 가진 ViewData에 SelectList가 있다는 것입니다. 이것은 올바르게 작동합니다.

// in my controller
ViewData["UserId"] = new SelectList(
    users,
    "UserId",
    "DisplayName",
    selectedUserId.Value); // this has a value

// in my view
<%=Html.DropDownList("UserId")%>


답변

이것이 팀이 수정해야하는 버그라고 생각하지 않는다면 MSDN은 문서를 개선해야합니다. 혼란스러운 것은 이것에 대한 잘못된 문서에서 비롯됩니다. MSDN 에서는 매개 변수 이름을 다음 과 같이 설명합니다 .

Type: System.String
The name of the form field to return.

이것은 생성하는 최종 html이 해당 매개 변수를 선택 입력의 이름으로 사용함을 의미합니다. 하지만 실제로는 그 이상을 의미합니다.

디자이너는 사용자가보기 모델을 사용하여 드롭 다운 목록을 표시하고 동일한 보기 모델로 다시 게시 할 것이라고 가정합니다 . 그러나 많은 경우에 우리는 그 가정을 실제로 따르지 않습니다.

위의 예를 사용하십시오.

public class Person {
    public int Id { get; set; }
    public string Name { get; set; }
}

가정을 따르면이 드롭 다운 목록 관련 뷰에 대한 뷰 모델을 정의해야합니다.

public class PersonsSelectViewModel{
    public string SelectedPersonId,
    public List<SelectListItem> Persons;
}

다시 게시 할 때 선택한 값만 다시 게시되므로 모델의 속성 SelectedPersonId에 다시 게시해야한다고 가정합니다. 즉, Html.DropDownList의 첫 번째 매개 변수 이름 은 ‘SelectedPersonId’여야합니다. 따라서 디자이너는 뷰에 모델 뷰를 표시 할 때 모델의 속성 SelectedPersonId가 해당 드롭 다운 목록의 기본값을 유지해야한다고 생각합니다. List <SelectListItem> Persons가 이미 Selected 플래그를 설정하여 선택 / 기본값을 표시한다고 생각하더라도 tml.DropDownList는 실제로이를 무시하고 고유 한 IEnumerable <SelectListItem>을 다시 빌드하고 이름에 따라 기본 / 선택된 항목을 설정합니다.

다음은 asp.net mvc의 코드입니다.

private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
            string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
            IDictionary<string, object> htmlAttributes)
{
    ...

    bool usedViewData = false;

    // If we got a null selectList, try to use ViewData to get the list of items.
    if (selectList == null)
    {
        selectList = htmlHelper.GetSelectData(name);
        usedViewData = true;
    }

    object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string));

    // If we haven't already used ViewData to get the entire list of items then we need to
    // use the ViewData-supplied value before using the parameter-supplied value.
    if (defaultValue == null && !String.IsNullOrEmpty(name))
    {
        if (!usedViewData)
        {
            defaultValue = htmlHelper.ViewData.Eval(name);
        }
        else if (metadata != null)
        {
            defaultValue = metadata.Model;
        }
    }

    if (defaultValue != null)
    {
        selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
    }

    ...

    return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
}

따라서 코드는 실제로 더 나아갔습니다. 모델에서 이름을 조회 할뿐만 아니라 뷰 데이터에서도 이름을 찾으면 selectList를 다시 빌드하고 원래 Selected를 무시합니다.

문제는 많은 경우에 우리는 실제로 그렇게 사용하지 않는다는 것입니다. 우리는 하나 / 여러 항목이 선택된 selectList를 true로 설정하기를 원합니다.

물론 솔루션은 간단합니다. 모델이나 뷰 데이터에없는 이름을 사용하십시오. 일치하는 항목을 찾을 수없는 경우 원래 selectList를 사용하고 원래 Selected가 적용됩니다.

하지만 여전히 mvc가 조건을 하나 더 추가하여 개선해야한다고 생각합니다.

if ((defaultValue != null) && (!selectList.Any(i=>i.Selected)))
{
    selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}

원래 selectList에 이미 하나의 Selected가있는 경우이를 무시하는 이유는 무엇입니까?

내 생각뿐입니다.


답변

이전 MVC 3 게시물의 코드는 작동하지 않지만 좋은 시작입니다. 내가 고칠 게. 이 코드를 테스트했으며 MVC 3 Razor C #에서 작동합니다.이 코드는 ViewModel 패턴을 사용하여 List<SelectListItem>.

모델 클래스

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

ViewModel 클래스

using System.Web.Mvc;

public class ProductListviewModel
{
    public List<SelectListItem> Products { get; set; }
}

컨트롤러 방법

public ViewResult List()
{
    var productList = new List<SelectListItem>();

    foreach (Product p in Products)
    {
        productList.Add(new SelectListItem
        {
            Value = p.ProductId.ToString(),
            Text = "Product: " + p.Name + " " + p.Price.ToString(),
            // To set the selected item use the following code 
            // Note: you should not set every item to selected
            Selected = true
        });
    }

    ProductListViewModel productListVM = new ProductListViewModeld();

    productListVM.Products = productList;

    return View(productListVM);
}

보기

@model MvcApp.ViewModels.ProductListViewModel

@using (Html.BeginForm())
{
    @Html.DropDownList("Products", Model.Products)
}

HTML 출력은 다음과 같습니다.

<select id="Products" name="Products">
    <option value="3">Product: Widget 10.00</option>
    <option value="4">Product: Gadget 5.95</option>
</select>

출력 형식에 따라 다릅니다. 이게 도움이 되길 바란다. 코드가 작동합니다.


답변

선택한 항목에 대한 모델이 아닌 ViewData 만 확인하므로 SelectExtensions 클래스의 버그로 보입니다. 따라서 트릭은 모델에서 선택한 항목을 속성 이름으로 ViewData 컬렉션으로 복사하는 것입니다.

이것은 MVC 포럼에서 제공 한 답변에서 가져온 것입니다. 또한 Kazi의 DropDownList 속성 을 사용 하는 블로그 게시물 에 더 완전한 답변이 있습니다 .

주어진 모델

public class ArticleType
{
   public Guid Id { get; set; }
   public string Description { get; set; }
}

public class Article
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public ArticleType { get; set; }
}

및 기본 뷰 모델

public class ArticleModel
{
     public Guid Id { get; set; }
     public string Name { get; set; }

     [UIHint("DropDownList")]
     public Guid ArticleType { get; set; }
}

그런 다음 다음과 같이 DropDownList 편집기 템플릿을 작성합니다.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script runat="server">
    IEnumerable<SelectListItem> GetSelectList()
    {
        var metaData = ViewData.ModelMetadata;
        if (metaData == null)
        {
            return null;
        }

        var selected = Model is SelectListItem ? ((SelectListItem) Model).Value : Model.ToString();
        ViewData[metaData.PropertyName] = selected;

        var key = metaData.PropertyName + "List";
        return (IEnumerable<SelectListItem>)ViewData[key];
    }
</script>
<%= Html.DropDownList(null, GetSelectList()) %>

이것은보기 모델의 ArticleType을 SelectListItem으로 변경하는 경우에도 작동하지만 Kazi의 블로그에 따라 유형 변환기를 구현하고 바인더가이를 단순 유형으로 처리하도록 등록해야합니다.

컨트롤러에서 우리는 …

public ArticleController
{
     ...
     public ActionResult Edit(int id)
     {
          var entity = repository.FindOne<Article>(id);
          var model = builder.Convert<ArticleModel>(entity);

          var types = repository.FindAll<ArticleTypes>();
          ViewData["ArticleTypeList"] = builder.Convert<SelectListItem>(types);

          return VIew(model);
     }
     ...
}


답변

문제는 드롭 박스가 적어도 ASP.NET MVC2 디자인이 기대하는 방식으로 목록 박스와 동일하게 작동하지 않는다는 것입니다. 드롭 박스는 여러 값을 선택할 수 있으므로 드롭 박스는 0 개 또는 1 개의 값만 허용합니다. 따라서 HTML에 엄격하므로 해당 값은 “선택됨”플래그로 옵션 목록에있는 것이 아니라 입력 자체에 있어야합니다.

다음 예를 참조하십시오.

<select id="combo" name="combo" value="id2">
  <option value="id1">This is option 1</option>
  <option value="id2" selected="selected">This is option 2</option>
  <option value="id3">This is option 3</option>
</select>

<select id="listbox" name="listbox" multiple>
  <option value="id1">This is option 1</option>
  <option value="id2" selected="selected">This is option 2</option>
  <option value="id3">This is option 3</option>
</select>

콤보에는 옵션이 선택되어 있지만 값 속성이 설정되어 있습니다. 따라서 ASP.NET MVC2가 드롭 박스를 렌더링하고 특정 값 (예 : 기본값 등)을 선택하도록하려면 다음과 같이 렌더링에 값을 지정해야합니다.

// in my view             
<%=Html.DropDownList("UserId", selectListItems /* (SelectList)ViewData["UserId"]*/, new { @Value = selectedUser.Id } /* Your selected value as an additional HTML attribute */)%>