Dlaczego Editor Template jest lepszy niż Partial View do tworzenia formularzy?

Wprowadzenie

W ASP.NET MVC rozbudowane formularze możemy utworzyć na różne sposoby. Na ogół staramy się wyrzucać do oddzielnych plików powtarzające się elementy, aby wykorzystywać je ponownie. Zauważyłem, że większość osób do tego celu wykorzystuje widoki Partial, które niestety często zamiast pomóc powodują dodatkowe problemy. W tym wpisie postaram Ci się pokazać, dlaczego w przypadku formularzy lepiej użyć Editor Templates niż widoków Partial.

Przykład

Zacznę od przykładu, abyś wiedział co chcemy osiągnąć i jaki będziemy mieli problem. W testowej aplikacji chcemy umożliwić edycję danych użytkownika. Użytkownik poza podstawowymi danymi ma również dane adresowe. Z racji tego, że te dane możemy mieć w różnych miejscach naszej aplikacji (np. sam użytkownik może mieć adres zamieszkania, adres korespondencyjny, adres do faktury), chcemy ten fragment formularza mieć zdefiniowany w innym pliku, aby używać do wielokrotnie.

Przygotowałem dwa viewmodele, jeden dla danych adresowych oraz drugi dla danych użytkownika:

Jak widać użytkownika zawiera właściwość typu AddressViewModel dla danych adresowych.

Zobaczmy jak zachowa się formularz zbudowany z wykorzystaniem widoku Partial.

Partial View

Do aplikacji dodałem bardzo prosty UsersController, który zawiera po dwie akcje dla każdego z przykładów (jedna akcja dla GET, druga dla POST). Akcje są bardzo proste i zwracają po prostu widok:

Ciekawsze rzeczy dzieją się w samych widokach. Dla adresu utworzyłem nowy widok Partial w katalogu Views/Shared, aby można było go używać wielokrotnie w różnych kontrolerach. Widok wygenerowało Visual Studio na podstawie viewmodelu, gdzie usunąłem z niego cały kod związanych z formularzem i zostawiłem same pola. Po tych zmianach widok wygląda tak:

Możemy go użyć teraz w innym widoku i dodać do jakiegoś istniejącego formularza. I tak też zrobiłem. Utworzyłem nowy widok w katalogu Views/Users dla akcji Partial z kontrolera UsersController. Podobnie jak wcześniej Visual Studio wygenerowania widok na podstawie viewmodelu, a na koniec użyłem widoku Partial (Html.Partial), aby wygenerować pola dla danych adresowych przez przyciskiem wysłania formularza. Ostateczny kod widoku wygląda tak:

Zwróć uwagę na linijkę 34, w której znajduje się użycie widoku Partial.

Sam widok w przeglądarce wygląda tak:

partial form

Aplikacja wyświetliła ładny formularz poprzez połączenie dwóch widoków.

Spójrzmy teraz na działanie tego widoku i w jaki sposób przesyła on dane do kontrolera.

Problem z widokiem Partial

Uzupełniłem dane w formularz oraz przesłałem go do kontrolera. W akcji dla POST postawiłem breakpointa i zobaczyłem jak uzupełnił dane model binder:

partial form debugger

Widać, że dane użytkownika (z UserViewModel) zostały ładnie wczytane do obiektu, natomiast dane adresowane już nie. Każda właściwość ma wartość null.

Dzieje się tak, ponieważ widok Partial w zły sposób generuje hmlta widoku. Najlepiej widać to w narzędziu developerskim chroma:

partial form html

Widać, że atrybut name inputa dla ulicy zawiera błędną wartość („Street”), zamiast („Address.Street”). Dlatego model binder próbuje ustawić właściwość Street w UserViewModel, przez co właściwość Street w Address jest pusta.

Jednym z sposobów rozwiązania tego problemu jest skorzystanie z tytułowy Editor Templates.

Darmowy kurs Visual Studio

Pracując z setkami programistów zauważyłem, że większość osób nie pracuje efektywnie w Visual Studio. W skrajnych przypadkach korzystali z kopiowania z wykorzystaniem menu Edit. Wiem, że to dziwne, ale naprawdę niektórzy tak pracują. Dlatego postanowiłem stworzyć kurs Visual Studio, aby pomóc koleżankom i kolegom w efektywniejszej pracy.

Przygotowałem 20 lekcji email, w których pokaże Ci w jaki sposób pracować efektywnej i szybciej w Visual Studio. Poznasz dodatkich, bez których nie wyobrażam sobie pracy w tym IDE.

Po więcej informacji zapraszam na dedykowaną stronę kursu: Darmowy Kurs Visual Studio.

Quiz C#

Ostatnio przygotowałem również quiz C#, w którym możesz sprawdzić swoją wiedzę. Podejmiesz wyzwanie?

Editor Template

Czym są szablony edycji? Jest to specjalny typ widoków partial, który jest przetwarzany przez ASP.NET MVC w trochę inny sposób. Przede wszystkim jest on związany między innymi z metodę EditorFor, którą używamy w widokach do generowania kontrolek dla poszczególnych pól. Gdy użyjemy tą metodę, ASP.NET MVC podczas generowania widoku szuka Editor Template dla danego typu danych (np. string, int, czy nawet dla naszych własnych typów).

Od strony kodu jest to taki sam widok Partial (nic w nim nie musimy zmieniać). Jedynie znajduje się w trochę innej lokalizacji oraz ma inną nazwę. Widoki te znajdują w się katalogu EditorTemplates (w katalogu Shared lub katalogu z widokami dla danego kontrolera). Natomiast nazwa widoku najlepiej jak byłaby taka sama jak nazwa typu, dla którego tworzymy szablon. W moim przypadku jest to AddressViewModel.cshtml:

editor templates solution explorer

W samy widoku użytkownika musimy użyć wspomnianą wyżej metodę EditorFor, aby ASP.NET MVC użyło szablon zamiast widoku Partial. Akcja EditorTemplate i widok dla niej w przykładzie zawiera tą zmianę. Sam poprawiony widok wygląda tak:

Jest on praktycznie identyczny jak poprzedni widok i różni się przede wszystkim linijką 34, w której jest „@Html.EditorFor(model => model.Address)” zamiast „@Html.Partial(„Address”, Model.Address)”.

Pod względem wizualnym wygenerowany widok jest identyczny i różni się on przede wszystkim wartościami atrybutu name inputów:

editor templates html

Gdy prześlemy dane do kontrola widać teraz, że wszystko ładnie zostało uzupełnione:

editor templates debugger

Przykład

Na githubie (https://github.com/danielplawgo/PartialForms) znajduje się przykład do tego wpisu. Testowa aplikacja jak widać powyżej zawiera dwie akcje ([adres]/Users/Partial oraz [adres]/Users/EditorTemplate), za pomocą których możesz sam sprawdzić różnicę między działaniem widoku Partial, a Editor Template.

Podsumowanie

Mam nadzieje, że po tym wpisie będzie wiedział dlaczego używanie widoków Partial do budowania formularzy to nie najlepszy pomysł. Niestety wiele razy widziałem aplikacje, w których programiści używali widoków Partial i później poświęcali wiele godzin, aby w jakiś sposób objeść ten problem. Na ogół tworzyli płaskie viewmodele, które później w kontrolerach mapowali (ręcznie lub za pomocą automappera) na zagnieżdżone obiekty. Utrzymanie tego kodu później było kosztowne, a można było to rozwiązać poprzez użycie Editor Templates.

W tym miejscu warto jeszcze wspomnieć, że w ASP.NET MVC istnieje drugi rodzaj szablonów (Display Templates), które działają w taki sam sposób (jedynie pliki znajdują w się w katalogu DisplayTemplates). Ich użycie odbywa się poprzez skorzystanie z metody DisplayFor.

A jakie są Twoje doświadczenia z widokami Partial oraz Editor Templates? Używasz tych drugich?

5 thoughts on “Dlaczego Editor Template jest lepszy niż Partial View do tworzenia formularzy?

  • Pingback: dotnetomaniak.pl
  • Jako alternatywa – w partialu można wywołać ViewData.TemplateInfo.HtmlFieldPrefix = „Address” i zadziała 😉

    • Hej Paweł!

      Masz racje można tak zrobić, ale według mnie na dłuższą metę takie rozwiązanie jest bardziej problematyczne niż Editor Templates. Szczególnie, gdy właściwości do adresu będą miały różne nazwy (np. użytkownik będzie miał 3 adresy: zamieszkania, korespondecyjny oraz do faktur).

  • Hmm wiekszym problemem wg mnie jest ze w takim projekcie dla jednej property potrzebujesz az 7 powtarzalnych linijek aby cos edytowac – sugerowalby jakis standardowy helper ktory by Ci wszystko dla jednej property robil (label, editorfor/inputbox, validationmessageFor) – bedzie bardziej przejrzyste i pozwoli pozbyc sie zbednych detali (dopoki nie robisz customowych rzeczy jest to wystarczajace)

    • Hej!
      Masz rację, jest to kolejny problem, która warto rozwiązać, aby tworzenie formularzy było jeszcze łatwiejsze. Porusze to w jednym z kolejnych wpisów.

      Dzięki za komentarz!

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *