…В примерах этого нет, но читатель легко это реализует сам…
ulu, статья на хабре 

Недавно пришлось реализовывать тот самый “smart way” отображения выпадающий списков, с поправкой на ASP.NET MVC 3. Это пост – короткий чеклист для реализации.

Попробуем отобразить выпадающий список для нескольких полей модели.

Основная модель:

ModelsMovie.cs

public class Movie
{
   
public int Id { get; set
; }
   
public int GenreId { get; set
; }
   
public int Year { get; set; }
}

Контроллер с двумя действиями – отображение формы и создание Movie:

ControllersMovieController.cs

public class MovieController : Controller
{
    [
HttpGet
]
   
public ActionResult
Create()
    {
       
return
View();
    }

    [
HttpPost
]
    [
ActionName("Create"
)]
   
public ActionResult CreateMovie(Movie
model)
    {
       
if (!this
.ModelState.IsValid)
        {
           
return
View(model);
        }
       
else
        {
           
// TODO: save model
            // this.Repository.Save(model);

           
return this.RedirectToAction("Details"
,
               
new
{ id = model.Id });
        }
    }

    [
HttpGet
]
   
public ActionResult
Details()
    {
       
// sample code to show the same Create form with preset values
        return View("Create", new Movie() { GenreId = 2, Year = 2000 });
    }
}

Представление для создания Movie. Создано стандартным scaffold-ом. Подключение скриптов валидации перенесено в Views/Shared/_Layout.cshtml:

ViewsMovieCreate.cshtml

@model MvcDropDowns.Models.Movie

@{
    ViewBag.Title =
"Create a Movie"
;
}

<h2>Create a Movie</h2>

@
using
(Html.BeginForm()) {
    @Html.ValidationSummary(
true
)
   
<fieldset>
        <legend>Movie</legend>

       
<div class="editor-label">
            @Html.LabelFor(model => model.GenreId)
       
</div>
        <div class="editor-field">
            @Html.EditorFor(model => model.GenreId)
            @Html.ValidationMessageFor(model => model.GenreId)
       
</div>

       
<div class="editor-label">
            @Html.LabelFor(model => model.Year)
       
</div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Year)
            @Html.ValidationMessageFor(model => model.Year)
       
</div>

       
<p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

Собираем, запускаем. Видим два текстовых поля. При нажатии на кнопку Create срабатывают клиентские валидаторы – поля не nullable.

image

Начнем превращать текстбоксы в выпадающие списки. Для начала, добавим UIHint и еще пару атрибутов для более человеческого отображения:

ModelsMovie.cs

public class Movie
{
   
public int Id { get; set
; }
       
    [
UIHint("Genre"
)]
    [
Display(Name="Genre"
)]
    [
DisplayFormat(NullDisplayText="---Please Select---"
)]
   
public int GenreId { get; set
; }

    [
UIHint("Year"
)]
    [
DisplayFormat(NullDisplayText="---Please Select---"
)]
   
public int Year { get; set; }
}

Следующий шаг – добавление шаблонов редактирования, с именами Genre и Year:

ViewsSharedEditorTemplatesGenre.cshtml

@model int?
@{
    Html.RenderAction(
"Genres", "List");
}

ViewsSharedEditorTemplatesYear.cshtml

@model int?
@{
    Html.RenderAction(
"Years", "List");
}

Единственное назначение этих шаблонов – вызов соответствующего действия контроллера List. Все остальное, включая имена полей, контроллер в состоянии получить самостоятельно. Основной соблазн при реализации – захардкодить название полей, или часть метаданных. Постараемся этого не допустить :)

ControllersListController.cs

public class ListController : Controller
{
   
public ActionResult
Genres()
    {
       
int? selectedGenreId = this.ControllerContext.ParentActionViewContext.ViewData.Model as int
?;

       
var genres = new List<Genre
>
        {
           
new Genre { Id = 1, DisplayName = "Comedy"
},
           
new Genre { Id = 2, DisplayName = "Horror"
},
           
new Genre { Id = 3, DisplayName = "Documentary"
}
        };

       
var model = new SelectList(genres, "Id", "DisplayName"
, selectedGenreId);

       
this
.ViewData.Model = model;
       
this.ViewData.ModelMetadata = this
.ControllerContext.ParentActionViewContext.ViewData.ModelMetadata;

       
return View("DropDown"
);
    }

   
public ActionResult
Years()
    {
       
int? selectedYear = this.ControllerContext.ParentActionViewContext.ViewData.Model as int
?;

       
var model = new SelectList(Enumerable
.Range(1990, 20), selectedYear);

       
this
.ViewData.Model = model;
       
this.ViewData.ModelMetadata = this
.ControllerContext.ParentActionViewContext.ViewData.ModelMetadata;

       
return View("DropDown");
    }
}

Последний этап – добавление представления DropDown. Вызываем Html.DropDown, передавая ему все подряд:

ViewsListDropDown.cshtml

@model SelectList
@{
   
var templateInfo = this
.ViewContext.ParentActionViewContext.ViewData.TemplateInfo;
}
@
Html.DropDownList(
    templateInfo.GetFullHtmlFieldName(
""
),
   
this
.Model,
   
this
.ViewData.ModelMetadata.NullDisplayText,
   
new
    {
        id = templateInfo.GetFullHtmlFieldId(
"")
    })

После запуска мы должны увидеть полноценные выпадающие списки:

image

У предложенной реализации есть один серьезный баг. Серверные валидаторы работают (как и раньше). Клиентские – не работают совсем. О том, как их починить, рассказано во второй части.

Исходный код к этой статье: MvcDropDowns.zip. Требует ASP.NET MVC 3.0 RC2 для запуска.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>