视图中的多个模型


302

我想在一个视图中有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; }
}

我是否需要制作另一个包含这两个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,非常感谢您提供不同选项的链接,四年后您发表了评论,它对我ViewBag
很有帮助

@stom仅供参考:帖子作者总是会收到通知,但是,如果您想通知其他人,则需要@像我在此一样在其姓名前加上姓名。
jpaugh

Answers:


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. 使用网站的Ajax部分变得更加独立

  4. iframes,但事实并非如此


2
如果两个文本框由于使用了partialviews而在表单上具有相同的名称,是否会有问题?
肖恩·麦克林

2
不,应该使用firebug之类的东西(在firefox上)单击元素本身,然后您会看到id =“ LoginViewModel_Email” name =“ LoginViewModel.Email”之类的东西,因此实际上它们是唯一的!视图模型应该是您所需要的,只需将每个页面发布到不同的URL,就可以了
Haroon

@Lol编码器实际上将是2种形式,每个视图模型一种,但是无论如何,如果您有2个或3个或更多具有相同名称的名称,您只会在服务器端获得一个具有该名称的数组(如果将其放在params中)后动作方法)
Omu

@Chuck Norris我正在使用asp.net mvc 4,并实现了您的partialviewresult技术,但@Html.RenderAction报告了一个错误,即Expression必须返回值
Deeptechtons 2012年

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将是驻留在当前View文件夹或Shared文件夹中的用户控件,并且需要这样的内容:

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

而且,您只有一个控制器动作,每个动作的查看和查看文件都完全不同,彼此之间不依赖任何内容。


4
就设计而言,这很有道理,但就效率而言,它是否不必经历mvc周期的3个完整周期?stackoverflow.com/questions/719027/renderaction-renderpartial/...
肖恩·麦克莱恩

6
是的,您是对的:这会为每个造成一个额外的完整MVC周期RenderAction。由于我的项目默认情况下始终包含该dll,所以我总是忘记了它在期货包中的作用。对于额外的mvc循环是否值得在设计方面进行分离,这完全取决于首选项和应用程序要求。很多时候,您可以缓存RenderAction结果,因此唯一需要您付出的就是通过控制器工厂进行的少量额外处理。
TheRightChoyce

我已经实现了以上..我想念的是什么?请帮助:stackoverflow.com/questions/9677818/...
diegohb

哇靠!这对我来说非常有效。我正在建立一个只有几个用户的内部站点...因此效率并不是我真正关心的问题。谢谢!
Derek Evermore

1
必须使用大括号使PartialView正常工作。
无效

113

另一种方法是使用:

@model Tuple<LoginViewModel,RegisterViewModel>

我已经为另一个示例说明了如何在视图和控制器中使用此方法:ASP MVC 3的一个视图中的两个模型

您可以使用以下代码来实现它:

在视图中:

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

在用于视图的控制器方法中,需要创建一个类型为Tuple的变量,然后将其传递给视图。

例:

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

或者,您可以使用值填充LoginViewModel和RegisterViewModel的两个实例,然后将其传递给视图。


谢谢,这是一种很好的处理方法!做了我所需要的。
Todd Davis

我已经尝试过了,但是如果我使用EditorForHiddenFor(理想情况下是我想使用的),则在调用Login1/ Login2控制器方法时不会设置模型属性。大概@Name=映射被忽略了。是否HiddenFor需要其他一些伎俩这种情况呢?
加里·查普曼

1
这根本不起作用-提交表单时您无法绑定到模型

@Hamid感谢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

我是否需要制作另一个包含这两个视图的视图?

答:没有

是否没有其他方法,例如(没有BigViewModel):

是的,您可以使用Tuple(在具有多个模型的视图中带魔术)。

码:

 @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

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中,例如,如果您有两个部分:

@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,一个视图中有多个模型?

但是,就我而言,他们在Controller中使用的linq查询对我不起作用。

这就是查询:

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

因此,“在您的视图中仅指定您正在使用视图模型的集合”对我也不起作用。

但是,对该解决方案进行一些细微改动确实对我有用。这是我的解决方案,以防万一。

这是我的视图模型,在其中我知道我只有一个团队,但是那个团队可能有多个面板(并且我的Models文件夹btw中有一个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. 创建模型中的一个新的类,属性LoginViewModelRegisterViewModel

    public class UserDefinedModel() 
    {
        property a1 as LoginViewModel 
        property a2 as RegisterViewModel 
    }
  2. 然后UserDefinedModel在您的视图中使用。


1
是的,这对我有用。然后我这样引用它:(模型在视图顶部声明。模型内部有2个模型:profile和emailstuff .. ..。@ Html.DisplayNameFor(model => model.profile.BlackoutBegin)在我在下面的@notso帖子中填充了其中一个模型的控制器,我不需要填充另一个模型,因为我只是将其用作输入
JustJohn 2016年

0

这是IEnumerable的简化示例。

我在视图上使用了两个模型:一个带有搜索条件的表单(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; }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.