ビュー内の複数のモデル


302

1つのビューに2つのモデルを表示したい。ページには、両方が含まれていますLoginViewModelRegisterViewModel

例えば

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; }
}

これら2つのViewModelを保持する別のViewModelを作成する必要がありますか?

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

検証属性をビューに転送する必要があります。これが、ViewModelsが必要な理由です。

(なし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)
 }


1
@saeed serpooshanあなたはコメントを投稿し、それは私を助けた4年後に、さまざまなオプションとのリンクのためにどうもありがとう、私はちょうど使用ViewBagビュー内のそれぞれのために、素晴らしい作品
shaijut

@stom Just a FYI:投稿者は常に通知を受け取りますが、他の人に通知したい場合@は、ここで行ったように、名前の前に置く必要があります。
jpaugh

回答:


260

たくさんの方法があります...

  1. BigViewModelを使用すると、次のことができます。

    @model BigViewModel    
    @using(Html.BeginForm()) {
        @Html.EditorFor(o => o.LoginViewModel.Email)
        ...
    }
  2. 2つの追加ビューを作成できます

    Login.cshtml

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

    そしてregister.cshtml 同じこと

    作成後、それらをメインビューでレンダリングし、viewmodel / viewdataを渡す必要があります。

    したがって、次のようになります。

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

    または

    @{Html.RenderPartial("login", Model.LoginViewModel)}
    @{Html.RenderPartial("register", Model.RegisterViewModel)}
  3. Webサイトのajax部分を使用すると、より独立したものになります

  4. iframes、しかしおそらくこれはそうではありません


2
パーシャルビューを使用しているために、フォーム上で2つのテキストボックスに同じ名前が付いている場合、問題ですか?
Shawn Mclean

2
いいえ、(firefoxの)firebugなどを使用して要素自体を細かくクリックすると、id = "LoginViewModel_Email" name = "LoginViewModel.Email"のようなものが表示されるため、実際には一意です。ビューモデルは必要なものでなければなりません。各ページを異なるURLに投稿するだけで問題ありません
Haroon

@Lolコーダーは実際にはビューモデルごとに1つずつの2つのフォームですが、同じ名前の2つまたは3つ以上がある場合、サーバー側でその名前の配列を取得します(paramsに配置した場合)ポストアクションメソッドの)
Omu

@Chuck Norris私はasp.net mvc 4を使用していて、partialviewresult手法を実装@Html.RenderActionしましたが、Expressionが値を返さなければならない
Deeptechtons

1
これがどのように機能するか説明できますか?これが問題の解決策であることは理解していますが、意味がわかりません。私はこの問題の類似した(わずかに異なる)例を持っていますが、それを回避する方法を理解できません。
アンドレ

127

Html.RenderActionこれを達成するには、PartialViewResultsを使用することをお勧めします。同じデータを表示できますが、各部分ビューには単一のビューモデルがあり、BigViewModel

したがって、ビューには次のようなものが含まれます。

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

ここでLoginRegisterは、コントローラーの両方のアクションが次のように定義されています。

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

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

LoginRegisterその後、いずれかの現在のビューのフォルダ内、または共有フォルダに存在するユーザーコントロールだろうと、このような何かをしたいと思います。

/Views/Shared/Login.cshtml:(または/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:(または/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)
}

そして、各アクションに対して1つのコントローラーアクション、ビュー、ビューファイルがあり、それぞれが完全に異なり、相互に依存していません。


4
これは設計の点では多くの意味がありますが、効率の点では、mvcサイクルの3つの完全なサイクルを通過する必要はありませんか?stackoverflow.com/questions/719027/renderaction-renderpartial/...
ショーン・マクリーンに

6
はい、正解です。これにより、ごとに追加の完全なMVCサイクルが発生しますRenderAction。私のプロジェクトには常にデフォルトでそのdllが含まれているので、私は常にフューチャーパックの一部を忘れています。追加のmvcサイクルが設計側で与える分離に値するかどうかに関しては、実際に好みとアプリケーション要件に依存します。多くの場合、RenderAction結果をキャッシュできるので、受ける唯一のヒットは、コントローラーファクトリーによるわずかな追加処理です。
TheRightChoyce

上記を実装しました。何が欠けていますか?してくださいヘルプ:stackoverflow.com/questions/9677818/...
diegohb

やばい!これは完全に私にとっては完全にうまくいきました。私は数人のユーザーだけで内部サイトを構築しています...だから効率は私の本当の関心事ではありません。ありがとうございました!
Derek Evermore 2013

1
PartialViewを機能させるには、中括弧を使用する必要がありました。
null

113

別の方法は使用することです:

@model Tuple<LoginViewModel,RegisterViewModel>

別の例として、ビューとコントローラーの両方でこのメソッドを使用する方法を説明しました:ASP MVC 3の1つのビューで2つのモデル

あなたのケースでは、次のコードを使用してそれを実装できます:

ビューで:

@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"})
}

フォームを作成するときに、各プロパティのName属性を手動で変更したことに注意しください。これを行う必要があります。そうしないと、値が関連するメソッドに送信されて処理されるときに、タイプモデルのメソッドのパラメーターに適切にマップされません。これらのフォームを個別に処理するには、個別のメソッドを使用することをお勧めします。この例では、Login1メソッドとLogin2メソッドを使用しました。Login1メソッドにはタイプRegisterViewModelのパラメーターが必要であり、Login2メソッドにはタイプLoginViewModelのパラメーターが必要です。

アクションリンクが必要な場合は、以下を使用できます。

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

ビューのコントローラーのメソッドでは、タプル型の変数を作成してビューに渡す必要があります。

例:

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

または、LoginViewModelとRegisterViewModelの2つのインスタンスに値を入力して、ビューに渡すこともできます。


これはそれを処理するための素晴らしい方法でした、ありがとう!私が必要とするものをしました。
トッドデイビス

私はこれを試しましたが、EditorForまたはHiddenFor(理想的には私が使用したいものです)を使用すると、Login1/ Login2コントローラーメソッドが呼び出されたときにモデルプロパティが設定されません。おそらく@Name=マッピングは無視されています。HiddenForこの状況では他のいくつかのトリックが必要ですか?
Gary Chapman、

1
これはまったく機能しません-フォームが送信されたときにモデルにバインドすることはできません

@Hamid Thanks Hamid、MVCの初心者にとって、これは私にとって最も単純な答えでした。ありがとうございました。
Harold_Finch

フォームを送信するときにモデルをどのようにバインドしましたか?
Mohammed Noureldin

28

複数のビューモデルを含むビューモデルを使用します。

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

あなたの見解では:

  @model MyProject.Web.ViewModels.UserViewModel

  @Html.LabelFor(model => model.User.UserName)
  @Html.LabelFor(model => model.Product.ProductName)
  @Html.LabelFor(model => model.Address.StreetName)

1
これは優れたソリューションであり、モデルの検証は問題なく機能します。ありがとう!
AFM-Horizo​​n

11

これら2つのビューを保持する別のビューを作成する必要がありますか?

回答:いいえ

次のような別の方法はありませんか(BigViewModelなし):

はい、タプルを使用できます(複数のモデルを持つビューに魔法をもたらします)。

コード:

 @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)
     }

これは、フォームを受け入れていたコントローラに適切にマッピングされませんか?「名前」を探す前に「アイテム」を探すのではないかと思いました。
SolidSnake4444 2017年

7

このModelCollection.csをモデルに追加します

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)];
   }
 }
}

コントローラ:

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);
  }
}

景色:

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

このアプローチが気に入ったのは、同じビューで異なるモデルを使用して、それらを交差させる必要がないためです。
Matt Small

6

それを行う簡単な方法

最初にすべてのモデルを呼び出すことができます

@using project.Models

次に、viewbagでモデルを送信します

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

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

とビューで

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

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

次に、モデルのようにこれを簡単に使用します


3

私のアドバイスは、大きなビューモデルを作成することです。

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

Index.cshtmlで、たとえば2つのパーシャルがある場合:

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

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

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

そしてコントローラーで:

model=new BigViewModel();
model.LoginViewModel=new LoginViewModel();
model.RegisterViewModel=new RegisterViewModel(); 

2

私のソリューションは、このstackoverflowページで提供された答えのようなものであると言いたい:ASP.NET MVC 4、1 つのビューに複数のモデル?

ただし、私の場合、コントローラーで使用されているlinqクエリが機能しませんでした。

これは言われたクエリです:

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

そのため、「ビューモデルのコレクションを使用していることをビューで指定する」だけでも機能しませんでした。

しかし、その解決策を少し変更するだけでうまくいきました。これが誰かを助ける場合の私の解決策はここにあります。

これが私のビューモデルです。チームは1つだけですが、そのチームには複数のボードがある場合があります(Modelsフォルダー内にViewModelsフォルダーがあるため、名前空間が含まれます)。

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

これが私のコントローラーです。これは、上記のリンクのソリューションとの最も大きな違いです。ViewModelを構築して、ビューに別の方法で送信します。

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);
        }

それから私の見解では、それをリストとして指定しません。「@model TaskBoard.Models.ViewModels.TeamBoards」を実行するだけで、チームのボードを反復処理するときに、それぞれにが必要になります。これが私の見解です:

@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>

私はASP.NET MVCにかなり慣れているので、これを理解するのに少し時間がかかりました。だから、私はこの投稿が誰かが彼らのプロジェクトのためにより短い時間枠でそれを理解するのに役立つことを願っています。:-)



1
  1. あなたのモデルとのプロパティで1つの新しいクラスを作成LoginViewModelしてRegisterViewModel

    public class UserDefinedModel() 
    {
        property a1 as LoginViewModel 
        property a2 as RegisterViewModel 
    }
  2. 次にUserDefinedModel、ビューで使用します。


1
はい、それは私のために働きます。それから私はこのようにそれを参照しました:(モデルはビューの上部で宣言されました。その中に2つのモデルがありました:プロファイルとemailstuff。。。。。@ Html.DisplayNameFor(model => model.profile.BlackoutBegin)In以下の@notsoポストを使用してモデルの1つを埋めたコントローラー入力用に使用していたため、他のモデルを埋める必要はありませんでした
JustJohn

0

これはIEnumerableの簡単な例です。

私はビューで2つのモデルを使用していました。検索条件付きのフォーム(SearchParamsモデル)と結果のグリッドで、同じビューにIEnumerableモデルと他のモデルを追加する方法に苦労しました。これが私が思いついたものです、これが誰かを助けることを願っています:

@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);
    }

SearchParamsViewModel.cs:

namespace DelegatePortal.ViewModels
{
    public class SearchParamsViewModel
    {
        public string FirstName { get; set; }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.