Bagikan melalui


Menggunakan Kelas ViewData dan Implementasikan ViewModel

oleh Microsoft

Unduh PDF

Ini adalah langkah 6 dari tutorial aplikasi "NerdDinner" gratis yang memandu cara membangun aplikasi web kecil, tetapi lengkap menggunakan ASP.NET MVC 1.

Langkah 6 menunjukkan cara mengaktifkan dukungan untuk skenario pengeditan formulir yang lebih kaya, dan juga membahas dua pendekatan yang dapat digunakan untuk meneruskan data dari pengontrol ke tampilan: ViewData dan ViewModel.

Jika Anda menggunakan ASP.NET MVC 3, kami sarankan Anda mengikuti tutorial Memulai MVC 3 atau MVC Music Store .

NerdDinner Langkah 6: ViewData dan ViewModel

Kami telah membahas sejumlah skenario posting formulir, dan membahas cara menerapkan dukungan buat, perbarui, dan hapus (CRUD). Kami sekarang akan mengambil implementasi DinnersController kami lebih lanjut dan mengaktifkan dukungan untuk skenario pengeditan formulir yang lebih kaya. Saat melakukan ini, kita akan membahas dua pendekatan yang dapat digunakan untuk meneruskan data dari pengontrol ke tampilan: ViewData dan ViewModel.

Meneruskan Data dari Pengontrol ke View-Templates

Salah satu karakteristik yang mendefinisikan pola MVC adalah "pemisahan kekhawatiran" yang ketat yang membantu menegakkan antara berbagai komponen aplikasi. Model, Pengontrol, dan Tampilan masing-masing memiliki peran dan tanggung jawab yang ditentukan dengan baik, dan mereka berkomunikasi satu sama lain dengan cara yang terdefinisi dengan baik. Ini membantu mempromosikan uji coba dan penggunaan kembali kode.

Ketika kelas Pengontrol memutuskan untuk merender respons HTML kembali ke klien, kelas pengontrol bertanggung jawab untuk secara eksplisit meneruskan ke templat tampilan semua data yang diperlukan untuk merender respons. Templat tampilan tidak boleh melakukan pengambilan data atau logika aplikasi apa pun - dan sebaliknya harus membatasi diri mereka untuk hanya memiliki kode penyajian yang didorong dari model/data yang diteruskan oleh pengontrol.

Saat ini data model yang diteruskan oleh kelas DinnersController kami ke templat tampilan kami sederhana dan lurus- daftar objek Makan Malam dalam kasus Index(), dan satu objek Makan Malam dalam kasus Details(), Edit(), Create() dan Delete(). Saat kami menambahkan lebih banyak kemampuan UI ke aplikasi kami, kami sering kali perlu meneruskan lebih dari sekadar data ini untuk merender respons HTML dalam templat tampilan kami. Misalnya, kita mungkin ingin mengubah bidang "Negara" dalam tampilan Edit dan Buat agar tidak menjadi kotak teks HTML menjadi daftar dropdown. Daripada membuat kode keras daftar dropdown nama negara dan wilayah dalam templat tampilan, kami mungkin ingin menghasilkannya dari daftar negara dan wilayah yang didukung yang kami isi secara dinamis. Kami akan membutuhkan cara untuk meneruskan objek Makan Malam dan daftar negara dan wilayah yang didukung dari pengontrol kami ke templat tampilan kami.

Mari kita lihat dua cara kita dapat mencapai ini.

Menggunakan Kamus ViewData

Kelas dasar Pengontrol mengekspos properti kamus "ViewData" yang dapat digunakan untuk meneruskan item data tambahan dari Pengontrol ke Tampilan.

Misalnya, untuk mendukung skenario di mana kita ingin mengubah kotak teks "Negara" dalam tampilan Edit dari kotak teks HTML menjadi daftar dropdown, kita dapat memperbarui metode tindakan Edit() kita untuk meneruskan (selain objek Makan Malam) objek SelectList yang dapat digunakan sebagai model daftar dropdown "Negara".

//
// GET: /Dinners/Edit/5

[Authorize]
public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    ViewData["Countries"] = new SelectList(PhoneValidator.AllCountries, dinner.Country);

    return View(dinner);
}

Konstruktor SelectList di atas menerima daftar negara dan wilayah untuk mengisi daftar drop-down dengan, serta nilai yang saat ini dipilih.

Kami kemudian dapat memperbarui templat tampilan Edit.aspx kami untuk menggunakan metode pembantu Html.DropDownList() alih-alih metode pembantu Html.TextBox() yang kami gunakan sebelumnya:

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

Metode pembantu Html.DropDownList() di atas mengambil dua parameter. Yang pertama adalah nama elemen formulir HTML ke output. Yang kedua adalah model "SelectList" yang kami lewati melalui kamus ViewData. Kami menggunakan kata kunci C# "sebagai" untuk mentransmisikan jenis dalam kamus sebagai SelectList.

Dan sekarang ketika kami menjalankan aplikasi kami dan mengakses URL /Dinners/Edit/1 dalam browser kami, kami akan melihat bahwa antarmuka pengguna edit kami telah diperbarui untuk menampilkan daftar dropdown negara dan wilayah alih-alih kotak teks:

Cuplikan layar antarmuka pengguna edit dengan daftar dropdown negara dan wilayah yang disorot dengan panah merah.

Karena kami juga merender templat Edit tampilan dari metode HTTP-POST Edit (dalam skenario ketika kesalahan terjadi), kami ingin memastikan bahwa kami juga memperbarui metode ini untuk menambahkan SelectList ke ViewData saat templat tampilan dirender dalam skenario kesalahan:

//
// POST: /Dinners/Edit/5

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    try {
    
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {
    
        ModelState.AddModelErrors(dinner.GetRuleViolations());

        ViewData["countries"] = new SelectList(PhoneValidator.AllCountries, dinner.Country);

        return View(dinner);
    }
}

Dan sekarang skenario edit DinnersController kami mendukung DropDownList.

Menggunakan Pola ViewModel

Pendekatan kamus ViewData memiliki manfaat terbilang cepat dan mudah diimplementasikan. Beberapa pengembang tidak suka menggunakan kamus berbasis string, karena kesalahan ketik dapat menyebabkan kesalahan yang tidak akan tertangkap pada waktu kompilasi. Kamus ViewData yang tidak diketik juga mengharuskan penggunaan operator "sebagai" atau transmisi saat menggunakan bahasa yang sangat diketik seperti C# dalam templat tampilan.

Pendekatan alternatif yang dapat kita gunakan adalah salah satu yang sering disebut sebagai pola "ViewModel". Saat menggunakan pola ini, kami membuat kelas dengan jenis kuat yang dioptimalkan untuk skenario tampilan spesifik kami, dan yang mengekspos properti untuk nilai/konten dinamis yang diperlukan oleh templat tampilan kami. Kelas pengontrol kami kemudian dapat mengisi dan meneruskan kelas yang dioptimalkan tampilan ini ke templat tampilan kami untuk digunakan. Ini memungkinkan keamanan jenis, pemeriksaan waktu kompilasi, dan intellisense editor dalam templat tampilan.

Misalnya, untuk mengaktifkan skenario pengeditan formulir makan malam, kita dapat membuat kelas "DinnerFormViewModel" seperti di bawah ini yang mengekspos dua properti yang sangat ditik: objek Makan Malam, dan model SelectList yang diperlukan untuk mengisi daftar dropdown "Negara":

public class DinnerFormViewModel {

    // Properties
    public Dinner     Dinner    { get; private set; }
    public SelectList Countries { get; private set; }

    // Constructor
    public DinnerFormViewModel(Dinner dinner) {
        Dinner = dinner;
        Countries = new SelectList(PhoneValidator.AllCountries, dinner.Country);
    }
}

Kami kemudian dapat memperbarui metode tindakan Edit() kami untuk membuat DinnerFormViewModel menggunakan objek Makan Malam yang kami ambil dari repositori kami, dan kemudian meneruskannya ke templat tampilan kami:

//
// GET: /Dinners/Edit/5

[Authorize]
public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);
    
    return View(new DinnerFormViewModel(dinner));
}

Kami kemudian akan memperbarui templat tampilan kami sehingga mengharapkan "DinnerFormViewModel" alih-alih objek "Makan Malam" dengan mengubah atribut "warisan" di bagian atas halaman edit.aspx seperti ini:

Inherits="System.Web.Mvc.ViewPage<NerdDinner.Controllers.DinnerFormViewModel>

Setelah kita melakukan ini, intellisense properti "Model" dalam templat tampilan kita akan diperbarui untuk mencerminkan model objek dari jenis DinnerFormViewModel yang kita lewati:

Cuplikan layar jendela editor kode dengan daftar dropdown dan item daftar Makan malam disorot dengan persegi panjang biru.

Cuplikan layar jendela editor kode dengan daftar dropdown dan item Daftar alamat disorot dengan persegi panjang putus-putus abu-abu.

Kita kemudian dapat memperbarui kode tampilan kita untuk mengerjakannya. Perhatikan di bawah ini bagaimana kami tidak mengubah nama elemen input yang kami buat (elemen formulir masih akan diberi nama "Judul", "Negara") - tetapi kami memperbarui metode Pembantu HTML untuk mengambil nilai menggunakan kelas DinnerFormViewModel:

<p>
    <label for="Title">Dinner Title:</label>
    <%= Html.TextBox("Title", Model.Dinner.Title) %>
    <%=Html.ValidationMessage("Title", "*") %>
</p>

<p>
    <label for="Country">Country:</label>
    <%= Html.DropDownList("Country", Model.Countries) %>                
    <%=Html.ValidationMessage("Country", "*") %>
</p>

Kami juga akan memperbarui metode edit posting kami untuk menggunakan kelas DinnerFormViewModel saat merender kesalahan:

//
// POST: /Dinners/Edit/5

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    try {
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {
        ModelState.AddModelErrors(dinner.GetRuleViolations());

        return View(new DinnerFormViewModel(dinner));
    }
}

Kami juga dapat memperbarui metode tindakan Create() kami untuk menggunakan kembali kelas DinnerFormViewModel yang sama persis untuk mengaktifkan DropDownList "Negara" dalam metode tersebut juga. Di bawah ini adalah implementasi HTTP-GET:

//
// GET: /Dinners/Create

public ActionResult Create() {

    Dinner dinner = new Dinner() {
        EventDate = DateTime.Now.AddDays(7)
    };

    return View(new DinnerFormViewModel(dinner));
}

Di bawah ini adalah implementasi metode HTTP-POST Create:

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Dinner dinner) {

    if (ModelState.IsValid) {

        try {
            dinner.HostedBy = "SomeUser";

            dinnerRepository.Add(dinner);
            dinnerRepository.Save();

            return RedirectToAction("Details", new { id=dinner.DinnerID });
        }
        catch {
            ModelState.AddModelErrors(dinner.GetRuleViolations());
        }
    }

    return View(new DinnerFormViewModel(dinner));
}

Dan sekarang layar Edit dan Buat kami mendukung daftar drop-down untuk memilih negara atau wilayah.

Kelas ViewModel berbentuk kustom

Dalam skenario di atas, kelas DinnerFormViewModel kami secara langsung mengekspos objek model Dinner sebagai properti, bersama dengan properti model SelectList yang mendukung. Pendekatan ini berfungsi dengan baik untuk skenario di mana UI HTML yang ingin kami buat dalam templat tampilan kami terkait relatif dekat dengan objek model domain kami.

Untuk skenario di mana ini tidak terjadi, salah satu opsi yang dapat Anda gunakan adalah membuat kelas ViewModel berbentuk kustom yang model objeknya lebih dioptimalkan untuk dikonsumsi oleh tampilan - dan yang mungkin terlihat sama sekali berbeda dari objek model domain yang mendasarinya. Misalnya, ini berpotensi mengekspos nama properti yang berbeda dan/atau properti agregat yang dikumpulkan dari beberapa objek model.

Kelas ViewModel berbentuk kustom dapat digunakan untuk meneruskan data dari pengontrol ke tampilan untuk dirender, serta untuk membantu menangani data formulir yang diposting kembali ke metode tindakan pengontrol. Untuk skenario ini nanti, Anda mungkin memiliki metode tindakan memperbarui objek ViewModel dengan data yang diposting formulir, lalu menggunakan instans ViewModel untuk memetakan atau mengambil objek model domain aktual.

Kelas ViewModel berbentuk kustom dapat memberikan banyak fleksibilitas, dan merupakan sesuatu untuk diselidiki setiap kali Anda menemukan kode penyajian dalam templat tampilan Anda atau kode posting formulir di dalam metode tindakan Anda mulai menjadi terlalu rumit. Ini sering kali merupakan tanda bahwa model domain Anda tidak sesuai dengan UI yang Anda buat, dan bahwa kelas ViewModel berbentuk kustom perantara dapat membantu.

Langkah Selanjutnya

Sekarang mari kita lihat bagaimana kita dapat menggunakan sebagian dan halaman master untuk menggunakan kembali dan berbagi UI di seluruh aplikasi kita.