Beberapa model dalam tampilan

302

Saya ingin memiliki 2 model dalam satu tampilan. Halaman berisi keduanya LoginViewModeldan RegisterViewModel.

misalnya

public class LoginViewModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

public class RegisterViewModel
{
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
}

Apakah saya perlu membuat ViewModel lain yang menampung 2 ViewModels ini?

public BigViewModel
{
    public LoginViewModel LoginViewModel{get; set;}
    public RegisterViewModel RegisterViewModel {get; set;}
}

Saya perlu atribut validasi untuk dibawa ke tampilan. Inilah sebabnya saya membutuhkan ViewModels.

Apakah tidak ada cara lain seperti (tanpa BigViewModel):

 @model ViewModel.RegisterViewModel
 @using (Html.BeginForm("Login", "Auth", FormMethod.Post))
 {
        @Html.TextBoxFor(model => model.Name)
        @Html.TextBoxFor(model => model.Email)
        @Html.PasswordFor(model => model.Password)
 }

 @model ViewModel.LoginViewModel
 @using (Html.BeginForm("Login", "Auth", FormMethod.Post))
 {
        @Html.TextBoxFor(model => model.Email)
        @Html.PasswordFor(model => model.Password)
 }
Shawn Mclean
sumber
3
melihat ini: codeproject.com/Articles/687061/...
S.Serpooshan
1
@saeed serpooshan, terima kasih banyak untuk tautannya dengan opsi yang berbeda, setelah 4 tahun Anda memposting komentar dan itu membantu saya, saya baru saja menggunakannya ViewBaguntuk masing-masing tampilan, berfungsi dengan baik
shaijut
@stom Just an FYI: penulis posting selalu mendapat notifikasi, tetapi jika Anda ingin memberi tahu orang lain, Anda harus meletakkan @di depan nama mereka, seperti yang saya lakukan di sini.
jpaugh

Jawaban:

260

Ada banyak cara ...

  1. dengan BigViewModel Anda, Anda melakukannya:

    @model BigViewModel    
    @using(Html.BeginForm()) {
        @Html.EditorFor(o => o.LoginViewModel.Email)
        ...
    }
    
  2. Anda dapat membuat 2 tampilan tambahan

    Login.cshtml

    @model ViewModel.LoginViewModel
    @using (Html.BeginForm("Login", "Auth", FormMethod.Post))
    {
        @Html.TextBoxFor(model => model.Email)
        @Html.PasswordFor(model => model.Password)
    }
    

    dan register.cshtml hal yang sama

    setelah penciptaan Anda harus merendernya di tampilan utama dan memberikannya viewmodel / viewdata

    jadi bisa seperti ini:

    @{Html.RenderPartial("login", ViewBag.Login);}
    @{Html.RenderPartial("register", ViewBag.Register);}
    

    atau

    @{Html.RenderPartial("login", Model.LoginViewModel)}
    @{Html.RenderPartial("register", Model.RegisterViewModel)}
    
  3. menggunakan bagian ajax dari situs web Anda menjadi lebih mandiri

  4. iframes, tapi mungkin ini bukan masalahnya

Omu
sumber
2
Apakah masalah jika 2 kotak teks memiliki nama yang sama pada formulir karena menggunakan tampilan parsial?
Shawn Mclean
2
Tidak, harus klik kanan pada elemen itu sendiri menggunakan sesuatu seperti pembakar (pada firefox) dan Anda akan melihat sesuatu seperti id = "LoginViewModel_Email" name = "LoginViewModel.Email", jadi sebenarnya mereka unik! Model tampilan harus sesuai dengan yang Anda butuhkan, cukup kirim setiap halaman ke URL yang berbeda dan Anda akan baik
Haroon
@Lol coder sebenarnya itu akan menjadi 2 bentuk, satu untuk setiap viewmodel, tetapi bagaimanapun jika Anda akan memiliki 2 atau 3 atau lebih dengan nama yang sama Anda hanya akan mendapatkan array dengan nama itu di sisi server (jika Anda meletakkannya di params dari metode post action)
Omu
@Chuck Norris Saya menggunakan asp.net mvc 4 dan menerapkan teknik partialviewresult Anda tetapi @Html.RenderActionmelaporkan kesalahan bahwa Ekspresi harus mengembalikan nilai
Deeptechtons
1
Bisakah Anda menjelaskan cara kerjanya? Saya mengerti ini adalah solusi untuk masalah ini, tetapi saya tidak dapat memahaminya. Saya memiliki contoh yang mirip (sedikit berbeda) dari masalah ini dan saya tidak tahu bagaimana cara mengatasinya.
Andre
127

Saya akan merekomendasikan menggunakan Html.RenderActiondan PartialViewResults untuk mencapai ini; itu akan memungkinkan Anda untuk menampilkan data yang sama, tetapi setiap tampilan parsial masih akan memiliki model tampilan tunggal dan menghilangkan kebutuhan untukBigViewModel

Jadi tampilan Anda mengandung sesuatu seperti berikut:

@Html.RenderAction("Login")
@Html.RenderAction("Register")

Di mana Login& Registerkedua tindakan di controller Anda didefinisikan seperti berikut:

public PartialViewResult Login( )
{
    return PartialView( "Login", new LoginViewModel() );
}

public PartialViewResult Register( )
{
    return PartialView( "Register", new RegisterViewModel() );
}

The Login& Registerkemudian akan kontrol pengguna yang berada baik dalam View folder saat ini, atau dalam folder Bersama dan ingin sesuatu seperti ini:

/Views/Shared/Login.cshtml: (atau /Views/MyView/Login.cshtml)

@model LoginViewModel
@using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
    @Html.TextBoxFor(model => model.Email)
    @Html.PasswordFor(model => model.Password)
}

/Views/Shared/Register.cshtml: (atau /Views/MyView/Register.cshtml)

@model ViewModel.RegisterViewModel
@using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
    @Html.TextBoxFor(model => model.Name)
    @Html.TextBoxFor(model => model.Email)
    @Html.PasswordFor(model => model.Password)
}

Dan di sana Anda memiliki tindakan pengontrol tunggal, melihat dan melihat file untuk setiap tindakan dengan masing-masing tindakan yang benar-benar berbeda dan tidak bergantung satu sama lain untuk apa pun.

TheRightChoyce
sumber
4
Ini masuk akal dalam hal merancang, tetapi dalam hal efisiensi, tidakkah harus melalui 3 siklus penuh dari siklus MVC? stackoverflow.com/questions/719027/renderaction-renderpartial/…
Shawn Mclean
6
Ya Anda benar: itu menyebabkan siklus MVC penuh tambahan untuk masing-masing RenderAction. Saya selalu lupa bagiannya dari paket berjangka karena proyek saya selalu menyertakan dll itu secara default. Yang benar-benar hingga preferensi dan persyaratan aplikasi untuk apakah siklus MVC tambahan layak pemisahan yang diberikannya pada sisi desain. Banyak kali Anda dapat men-cache RenderActionhasil sehingga satu-satunya hit yang Anda ambil adalah sedikit pemrosesan ekstra melalui pabrik pengontrol.
TheRightChoyce
Saya sudah menerapkan hal di atas .. apa yang saya lewatkan? Tolong bantu: stackoverflow.com/questions/9677818/…
diegohb
Sial! Ini bekerja dengan baik bagi saya tepat di luar pintu gerbang. Saya sedang membangun situs internal dengan hanya beberapa pengguna ... jadi efisiensi bukan masalah saya. TERIMA KASIH!
Derek Evermore
1
Harus menggunakan kurung keriting untuk membuat PartialView bekerja.
null
113

Cara lain adalah dengan menggunakan:

@model Tuple<LoginViewModel,RegisterViewModel>

Saya telah menjelaskan cara menggunakan metode ini baik dalam tampilan dan pengontrol untuk contoh lain: Dua model dalam satu tampilan di ASP MVC 3

Dalam kasus Anda, Anda bisa menerapkannya menggunakan kode berikut:

Dalam tampilan:

@using YourProjectNamespace.Models;
@model Tuple<LoginViewModel,RegisterViewModel>

@using (Html.BeginForm("Login1", "Auth", FormMethod.Post))
{
        @Html.TextBoxFor(tuple => tuple.Item2.Name, new {@Name="Name"})
        @Html.TextBoxFor(tuple => tuple.Item2.Email, new {@Name="Email"})
        @Html.PasswordFor(tuple => tuple.Item2.Password, new {@Name="Password"})
}

@using (Html.BeginForm("Login2", "Auth", FormMethod.Post))
{
        @Html.TextBoxFor(tuple => tuple.Item1.Email, new {@Name="Email"})
        @Html.PasswordFor(tuple => tuple.Item1.Password, new {@Name="Password"})
}

Perhatikan bahwa saya telah secara manual mengubah atribut Name untuk setiap properti ketika membangun formulir. Ini perlu dilakukan, jika tidak maka tidak akan bisa dipetakan dengan benar ke parameter model tipe ketika nilai dikirim ke metode terkait untuk diproses. Saya akan menyarankan menggunakan metode terpisah untuk memproses formulir ini secara terpisah, untuk contoh ini saya menggunakan metode Login1 dan Login2. Metode Login1 harus memiliki parameter bertipe RegisterViewModel dan Login2 memerlukan parameter bertipe LoginViewModel.

jika diperlukan actionlink, Anda dapat menggunakan:

@Html.ActionLink("Edit", "Edit", new { id=Model.Item1.Id })

dalam metode controller untuk tampilan, variabel tipe Tuple perlu dibuat dan kemudian diteruskan ke tampilan.

Contoh:

public ActionResult Details()
{
    var tuple = new Tuple<LoginViewModel, RegisterViewModel>(new LoginViewModel(),new RegisterViewModel());
    return View(tuple);
}

atau Anda dapat mengisi dua instance dari LoginViewModel dan RegisterViewModel dengan nilai dan kemudian meneruskannya ke tampilan.

Hamid Tavakoli
sumber
Ini cara yang bagus untuk menanganinya, terima kasih! Melakukan apa yang saya butuhkan.
Todd Davis
Saya sudah mencoba ini, tetapi jika saya menggunakan EditorForatau HiddenFor(yang idealnya ingin saya gunakan) properti model tidak diatur ketika metode Login1/ Login2controller dipanggil. Agaknya @Name=pemetaan itu diabaikan. Apakah HiddenFormemerlukan beberapa trik lain untuk situasi ini?
Gary Chapman
1
Ini tidak akan berfungsi sama sekali - Anda tidak dapat mengikat ke model ketika formulir dikirimkan
@Hamid Terima kasih Hamid, untuk seorang pemula di MVC, ini adalah jawaban yang paling sederhana bagi saya. Terima kasih.
Harold_Finch
Bagaimana Anda mengikat Model saat mengirimkan formulir?
Mohammed Noureldin
28

Gunakan model tampilan yang berisi beberapa model tampilan:

   namespace MyProject.Web.ViewModels
   {
      public class UserViewModel
      {
          public UserDto User { get; set; }
          public ProductDto Product { get; set; }
          public AddressDto Address { get; set; }
      }
   }

Menurut Anda:

  @model MyProject.Web.ViewModels.UserViewModel

  @Html.LabelFor(model => model.User.UserName)
  @Html.LabelFor(model => model.Product.ProductName)
  @Html.LabelFor(model => model.Address.StreetName)
Yini
sumber
1
Ini adalah solusi hebat, dan validasi model masih berfungsi tanpa keraguan. Terima kasih!
AFM-Horizon
11

Apakah saya perlu membuat tampilan lain yang memiliki 2 tampilan ini?

Jawab: Tidak

Apakah tidak ada cara lain seperti (tanpa BigViewModel):

Ya , Anda dapat menggunakan Tuple (menghadirkan sulap yang memiliki banyak model).

Kode:

 @model Tuple<LoginViewModel, RegisterViewModel>


    @using (Html.BeginForm("Login", "Auth", FormMethod.Post))
    {
     @Html.TextBoxFor(tuple=> tuple.Item.Name)
     @Html.TextBoxFor(tuple=> tuple.Item.Email)
     @Html.PasswordFor(tuple=> tuple.Item.Password)
    }


    @using (Html.BeginForm("Login", "Auth", FormMethod.Post))
     {
      @Html.TextBoxFor(tuple=> tuple.Item1.Email)
      @Html.PasswordFor(tuple=> tuple.Item1.Password)
     }
VCody
sumber
Bukankah ini tidak memetakan dengan benar ke controller yang menerima formulir? Saya pikir itu akan mencari "Barang" dalam kasus Anda sebelum mencari "nama".
SolidSnake4444
7

Tambahkan ModelCollection.cs ini ke Model Anda

using System;
using System.Collections.Generic;

namespace ModelContainer
{
  public class ModelCollection
  {
   private Dictionary<Type, object> models = new Dictionary<Type, object>();

   public void AddModel<T>(T t)
   {
      models.Add(t.GetType(), t);
   }

   public T GetModel<T>()
   {
     return (T)models[typeof(T)];
   }
 }
}

Pengendali:

public class SampleController : Controller
{
  public ActionResult Index()
  {
    var model1 = new Model1();
    var model2 = new Model2();
    var model3 = new Model3();

    // Do something

    var modelCollection = new ModelCollection();
    modelCollection.AddModel(model1);
    modelCollection.AddModel(model2);
    modelCollection.AddModel(model3);
    return View(modelCollection);
  }
}

Pandangan:

enter code here
@using Models
@model ModelCollection

@{
  ViewBag.Title = "Model1: " + ((Model.GetModel<Model1>()).Name);
}

<h2>Model2: @((Model.GetModel<Model2>()).Number</h2>

@((Model.GetModel<Model3>()).SomeProperty
Morten Frederiksen
sumber
Saya suka pendekatan ini karena memungkinkan saya untuk menggunakan model yang berbeda dalam tampilan yang sama tanpa perlu mereka berpotongan
Matt Small
6

cara sederhana untuk melakukan itu

kita bisa memanggil semua model terlebih dahulu

@using project.Models

kemudian kirim model Anda dengan viewbag

// for list
ViewBag.Name = db.YourModel.ToList();

// for one
ViewBag.Name = db.YourModel.Find(id);

dan dalam pandangan

// for list
List<YourModel> Name = (List<YourModel>)ViewBag.Name ;

//for one
YourModel Name = (YourModel)ViewBag.Name ;

maka dengan mudah gunakan Model seperti ini

Pnsadeghy
sumber
3

Saran saya adalah membuat model tampilan besar:

public BigViewModel
{
    public LoginViewModel LoginViewModel{get; set;}
    public RegisterViewModel RegisterViewModel {get; set;}
}

Di Index.cshtml Anda, jika misalnya Anda memiliki 2 bagian:

@addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers
@model .BigViewModel

@await Html.PartialAsync("_LoginViewPartial", Model.LoginViewModel)

@await Html.PartialAsync("_RegisterViewPartial ", Model.RegisterViewModel )

dan di controller:

model=new BigViewModel();
model.LoginViewModel=new LoginViewModel();
model.RegisterViewModel=new RegisterViewModel(); 
alin
sumber
2

Saya ingin mengatakan bahwa solusi saya seperti jawaban yang diberikan pada halaman stackoverflow ini: ASP.NET MVC 4, beberapa model dalam satu tampilan?

Namun, dalam kasus saya, kueri linq yang mereka gunakan di Controller mereka tidak berfungsi untuk saya.

Ini dikatakan permintaan:

var viewModels = 
        (from e in db.Engineers
         select new MyViewModel
         {
             Engineer = e,
             Elements = e.Elements,
         })
        .ToList();

Akibatnya, "dalam tampilan Anda, tentukan saja bahwa Anda menggunakan koleksi model tampilan" juga tidak berfungsi untuk saya.

Namun, sedikit variasi pada solusi itu berhasil bagi saya. Ini solusi saya kalau-kalau ini bisa membantu siapa saja.

Berikut ini adalah model tampilan saya di mana saya tahu saya hanya akan memiliki satu tim tetapi tim itu mungkin memiliki beberapa papan (dan saya memiliki folder ViewModels dalam folder Model saya btw, maka namespace):

namespace TaskBoard.Models.ViewModels
{
    public class TeamBoards
    {
        public Team Team { get; set; }
        public List<Board> Boards { get; set; }
    }
}

Sekarang ini adalah pengontrol saya. Ini adalah perbedaan paling signifikan dari solusi dalam tautan yang dirujuk di atas. Saya membangun ViewModel untuk mengirim ke tampilan secara berbeda.

public ActionResult Details(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }

            TeamBoards teamBoards = new TeamBoards();
            teamBoards.Boards = (from b in db.Boards
                                 where b.TeamId == id
                                 select b).ToList();
            teamBoards.Team = (from t in db.Teams
                               where t.TeamId == id
                               select t).FirstOrDefault();

            if (teamBoards == null)
            {
                return HttpNotFound();
            }
            return View(teamBoards);
        }

Maka dalam pandangan saya, saya tidak menentukannya sebagai daftar. Saya hanya melakukan "@model TaskBoard.Models.ViewModels.TeamBoards" Maka saya hanya perlu satu untuk masing-masing ketika saya beralih ke papan Tim. Inilah pandangan saya:

@model TaskBoard.Models.ViewModels.TeamBoards

@{
    ViewBag.Title = "Details";
}

<h2>Details</h2>

<div>
    <h4>Team</h4>
    <hr />


    @Html.ActionLink("Create New Board", "Create", "Board", new { TeamId = @Model.Team.TeamId}, null)
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => Model.Team.Name)
        </dt>

        <dd>
            @Html.DisplayFor(model => Model.Team.Name)
            <ul>
                @foreach(var board in Model.Boards)
                { 
                    <li>@Html.DisplayFor(model => board.BoardName)</li>
                }
            </ul>
        </dd>

    </dl>
</div>
<p>
    @Html.ActionLink("Edit", "Edit", new { id = Model.Team.TeamId }) |
    @Html.ActionLink("Back to List", "Index")
</p>

Saya cukup baru untuk ASP.NET MVC jadi butuh sedikit waktu untuk mencari tahu. Jadi, saya berharap posting ini membantu seseorang untuk proyek mereka dalam jangka waktu yang lebih pendek. :-)

Tidak begitu
sumber
2

Anda selalu dapat melewatkan objek kedua dalam ViewBag atau Lihat Data.

Nada N. Hantouli
sumber
1
  1. Buat satu kelas baru di model dan properti Anda LoginViewModeldan RegisterViewModel:

    public class UserDefinedModel() 
    {
        property a1 as LoginViewModel 
        property a2 as RegisterViewModel 
    }
  2. Kemudian gunakan UserDefinedModeldalam pandangan Anda.

Andrew
sumber
1
ya, itu bekerja untuk saya. kemudian saya mereferensikannya seperti ini: (model dideklarasikan di bagian atas tampilan. Ada 2 model di dalamnya: profil, dan emailstuff ... ... @ Html.DisplayNameFor (model => model.profile.BlackoutBegin) Di controller saya mengisi salah satu model menggunakan @notso post di bawah ini. Saya tidak perlu mengisi yang lain karena saya hanya menggunakannya untuk input.
JustJohn
0

Ini adalah contoh yang disederhanakan dengan IEnumerable.

Saya menggunakan dua model pada tampilan: formulir dengan kriteria pencarian (model SearchParams), dan kotak untuk hasil, dan saya berjuang dengan cara menambahkan model IEnumerable dan model lainnya pada tampilan yang sama. Inilah yang saya buat, semoga ini membantu seseorang:

@using DelegatePortal.ViewModels;

@model SearchViewModel

@using (Html.BeginForm("Search", "Delegate", FormMethod.Post))
{

                Employee First Name
                @Html.EditorFor(model => model.SearchParams.FirstName,
new { htmlAttributes = new { @class = "form-control form-control-sm " } })

                <input type="submit" id="getResults" value="SEARCH" class="btn btn-primary btn-lg btn-block" />

}
<br />
    @(Html
        .Grid(Model.Delegates)
        .Build(columns =>
        {
            columns.Add(model => model.Id).Titled("Id").Css("collapse");
            columns.Add(model => model.LastName).Titled("Last Name");
            columns.Add(model => model.FirstName).Titled("First Name");
        })

...)

SearchViewModel.cs:

namespace DelegatePortal.ViewModels
{
    public class SearchViewModel
    {
        public IEnumerable<DelegatePortal.Models.DelegateView> Delegates { get; set; }

        public SearchParamsViewModel SearchParams { get; set; }
....

DelegateController.cs:

// GET: /Delegate/Search
    public ActionResult Search(String firstName)
    {
        SearchViewModel model = new SearchViewModel();
        model.Delegates = db.Set<DelegateView>();
        return View(model);
    }

    // POST: /Delegate/Search
    [HttpPost]
    public ActionResult Search(SearchParamsViewModel searchParams)
    {
        String firstName = searchParams.FirstName;
        SearchViewModel model = new SearchViewModel();

        if (firstName != null)
            model.Delegates = db.Set<DelegateView>().Where(x => x.FirstName == firstName);

        return View(model);
    }

CariParamsViewModel.cs:

namespace DelegatePortal.ViewModels
{
    public class SearchParamsViewModel
    {
        public string FirstName { get; set; }
    }
}
cinta hidup
sumber