Lista checkbox-ów w ASP.NET MVC

ASP.NET MVC daje nam dostęp do wielu niezbędnych w tworzeniu webowych aplikacji komponentów – również jeśli chodzi o interfejs użytkownika. W toku pracy okazało się, że potrzebna jest mi funkcjonalność listy checkboxów  – np. do określenia gatunków filmowych, do których film jest przypisany. No tu niestety zawód srogi mnie spotkał – nie ma wbudowanej kontrolki spełniającej taką zachciankę. Na szczęście okazało się, że z większością problemów nie spotykam się jako pierwszy człowiek w historii i pomocna kontrolka powstała:)

Jest dostępna w repozytorium NuGeta pod nazwą MvcCheckBoxList – więc stamtąd ją pobieram i instaluję do projektu VodSearcher.

Musimy jednak przygotować dla niej grunt. Kontrolkę będziemy wykorzystywać podczas dodawania nowego i edycji istniejącego obiektu w bazie. Utworzyłem więc kolejny obiekt Dto:

public class MovieCreateOrUpdateDto
{
    public int ID { get; set; }
    [DisplayName("Tytuł")]
    public string Title { get; set; }
    [DisplayName("Rok produkcji")]
    public int ProductionYear { get; set; }
    [DisplayName("Gatunki")]
    public List<GenreSelectionDto> Genres { get; set; }
    public List<GenreSelectionDto> CurrentGenres { get; set; }
    public int[] SelectedGenres { get; set; }
}

Większość właściwości jest oczywista – wyjaśnię jedynie te dotyczące gatunków. Genres to lista wszystkich zdefiniowanych w bazie gatunków filmowych, CurrentGenres to gatunki, do których film jest przypisany, a SelectedGenres to tablica, do której trafią zaznaczone przez użytkownika gatunki (ich identyfikatory). GenreSelectionDto to znów prosty obiekt z informacją o identyfikatorze i nazwie gatunku filmowego.

Widzimy, że zarówno Genres (dla nowych i istniejących filmów) jak i CurrentGenres (dla istniejących filmów) musimy wypełnić przed przekazaniem obiektu do widoku. Służy do tego metoda GetCreateOrUpdateDto(int id) w serwisie MovieService. Wyciągamy w niej informacje z kilku tabel bazy danych – potrzebujemy więc mapowań:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Movie, MovieCreateOrUpdateDto>()
        .ForMember(dest => dest.CurrentGenres,
        m => m.MapFrom(src => src.MovieGenres.ToList()));
    cfg.CreateMap<Genre, GenreSelectionDto>();
    cfg.CreateMap<MovieGenre, GenreSelectionDto>()  
        .ForMember(dest => dest.ID, m => m.MapFrom(src => src.GenreID))
        .ForMember(dest => dest.Name, m => m.MapFrom(src => src.Genre.Name));
});

Następnie korzystając utworzonego mappera oraz projekcji, o której pisałem tutaj, tworzymy i wypełniamy obiekt Dto:

var mapper = config.CreateMapper();
List<GenreSelectionDto> genres = appContext.Genres.ProjectTo<GenreSelectionDto>(config).ToList();
MovieCreateOrUpdateDto dto = new MovieCreateOrUpdateDto();
dto.Genres = genres;

Movie movie = appContext.Movies.Where(m => m.ID == id).FirstOrDefault();
if (movie != null)
{
    mapper.Map<Movie, MovieCreateOrUpdateDto>(movie, dto);
}
else
{
    dto.CurrentGenres = new List<GenreSelectionDto>();
}
return dto;

Co się dzieje w widoku? Tutaj (zarówno w widoku Create jak i Edit) musimy dołączyć nową kontrolkę:

@using MvcCheckBoxList.Model

i już możemy jej użyć:

@Html.CheckBoxListFor(model => model.SelectedGenres,
    model => model.Genres,
    x => x.ID,
    x => x.Name,
    model => model.CurrentGenres
)

Jako parametry określamy zdefiniowane wcześniej listy gatunków (wszystkich, przypisanych i docelowych) oraz właściwość będącą identyfikatorem checkboxa i jego nazwą.

W interfejsie użytkownika będzie to wyglądać, o tak:

014a

Zaznaczone gatunki zostaną wrzucone do tablicy SelectedGenres i będą gotowe do przesłania i zapisania w bazie danych.

Marcin

Dodaj komentarz