【问题标题】:Multiple models in a view视图中的多个模型
【发布时间】:2011-06-13 10:27:44
【问题描述】:

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

我需要将验证属性提交给视图。这就是我需要 ViewModel 的原因。

有没有其他方式比如(没有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)
 }

【问题讨论】:

  • @saeed serpooshan,非常感谢您提供具有不同选项的链接,4 年后您发表了评论,这对我有帮助,我只是在视图中使用 ViewBag,效果很好
  • @stom 仅供参考:帖子作者总是会收到通知,但如果你想通知其他人,你需要在他们的名字前面加上@,就像我在这里所做的那样。

标签: asp.net-mvc asp.net-mvc-3


【解决方案1】:

有很多方法...

  1. 与您的 BigViewModel 你这样做:

    @model BigViewModel    
    @using(Html.BeginForm()) {
        @Html.EditorFor(o => o.LoginViewModel.Email)
        ...
    }
    
  2. 您可以创建 2 个额外的视图

    登录.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 个文本框在表单上具有相同的名称,会不会有问题?
  • 不,应该很好 - 使用类似 firebug(在 firefox 上)点击元素本身,你会看到类似 id="LoginViewModel_Email" name = "LoginViewModel.Email" 的东西,所以实际上他们是独一无二的!视图模型应该是您所需要的,只需将每个页面发布到不同的 URL 就可以了
  • @Lol 编码器实际上将是 2 种形式,每个视图模型一个,但无论如何,如果您有 2 或 3 个或更多同名,您只会在服务器端获得一个具有该名称的数组(如果你把它放在 post action 方法的参数中)
  • @Chuck Norris 我正在使用 asp.net mvc 4 并实现了您的 partialviewresult 技术,但 @Html.RenderAction 报告了一个错误,即 Expression must return a value
  • 您能解释一下这是如何工作的吗?我知道这是解决问题的方法,但我无法理解。我有一个类似(略有不同)的这个问题的例子,我不知道如何解决它。
【解决方案2】:

我建议使用 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)
}

你有一个控制器动作,每个动作的视图和查看文件,每个动作完全不同,并且在任何事情上不相互依赖。

【讨论】:

  • 这在设计上很有道理,但是从效率上来说,不是要经过3个完整的mvc循环吗? stackoverflow.com/questions/719027/renderaction-renderpartial/…
  • 是的,你是对的:它会为每个 RenderAction 导致一个额外的完整 MVC 循环。我总是忘记它是期货包的一部分,因为我的项目默认情况下总是包含那个 dll。至于额外的 mvc 循环是否值得它在设计方面提供的分离,这真的取决于偏好和应用程序要求。很多时候,您可以缓存 RenderAction 结果,因此您受到的唯一打击是通过控制器工厂进行的轻微额外处理。
  • 我已经实现了上述..我错过了什么?请帮忙:stackoverflow.com/questions/9677818/…
  • 天哪!这对我来说非常有效。我正在建立一个只有几个用户的内部站点......所以效率并不是我真正关心的问题。谢谢!
  • 必须使用大括号才能让 PartialView 工作。
【解决方案3】:

另一种方法是使用:

@model Tuple<LoginViewModel,RegisterViewModel>

我已经在另一个示例中解释了如何在视图和控制器中使用此方法:Two models in one view in 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 的两个实例,然后将其传递给视图。

【讨论】:

  • 这是处理它的好方法,谢谢!做了我需要的。
  • 我已经尝试过了,但是如果我使用 EditorForHiddenFor (这是我想要使用的理想状态),当 Login1/Login2控制器方法被调用。大概@Name= 映射被忽略了。 HiddenFor 在这种情况下是否需要其他技巧?
  • 这根本不起作用 - 提交表单时无法绑定到模型
  • @Hamid 感谢 Hamid,对于 MVC 的新手来说,这对我来说是最简单的答案。谢谢。
  • 提交表单时是如何绑定Model的?
【解决方案4】:

使用包含多个视图模型的视图模型:

   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)

【讨论】:

  • 这是一个很好的解决方案,模型验证仍然可以毫无疑虑地工作。谢谢!
【解决方案5】:

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

答案:否

是否还有其他方式,例如(没有 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)
     }

【讨论】:

  • 这不会正确映射到接受表单的控制器吗?我认为它会在查找“名称”之前在您的情况下查找“项目”。
【解决方案6】:

将此 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

【讨论】:

  • 我喜欢这种方法,因为它允许我在同一个视图中使用不同的模型,而无需让它们相交。
【解决方案7】:

一个简单的方法来做到这一点

我们可以先调用所有模型

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

然后像模型一样轻松使用它

【讨论】:

    【解决方案8】:

    我的建议是制作一个大视图模型:

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

    【讨论】:

      【解决方案9】:

      我想说我的解决方案就像这个 stackoverflow 页面上提供的答案:ASP.NET MVC 4, multiple models in one view?

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

      这是说查询:

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

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

      但是,该解决方案的细微变化确实对我有用。这是我的解决方案,以防万一这对任何人都有帮助。

      这是我的视图模型,我知道我将只有一个团队,但该团队可能有多个板(顺便说一句,我的 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”然后当我迭代团队的董事会时,我只需要一个 for each。这是我的看法:

      @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 还很陌生,所以我花了一点时间才弄清楚这一点。所以,我希望这篇文章能帮助人们在更短的时间内为他们的项目弄清楚。 :-)

      【讨论】:

        【解决方案10】:
        1. 在您的模型和LoginViewModelRegisterViewModel 的属性中创建一个新类:

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

        【讨论】:

        • 是的,这对我有用。然后我像这样引用它:(模型在视图的顶部声明。里面有 2 个模型:profile 和 emailstuff ...... @Html.DisplayNameFor(model => model.profile.BlackoutBegin) 在控制器我使用下面的@notso 帖子填充了其中一个模型。我不需要填充另一个,因为我只是将它用于输入。
        【解决方案11】:

        您始终可以在 ViewBag 或 View Data 中传递第二个对象。

        【讨论】:

          【解决方案12】:

          这是一个使用 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; }
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-02-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多