I want to have 2 models in one view. The page contains both LoginViewModel
and RegisterViewModel
.
e.g.
pub
I want to say that my solution was like the answer provided on this stackoverflow page: ASP.NET MVC 4, multiple models in one view?
However, in my case, the linq query they used in their Controller did not work for me.
This is said query:
var viewModels =
(from e in db.Engineers
select new MyViewModel
{
Engineer = e,
Elements = e.Elements,
})
.ToList();
Consequently, "in your view just specify that you're using a collection of view models" did not work for me either.
However, a slight variation on that solution did work for me. Here is my solution in case this helps anyone.
Here is my view model in which I know I will have just one team but that team may have multiple boards (and I have a ViewModels folder within my Models folder btw, hence the namespace):
namespace TaskBoard.Models.ViewModels
{
public class TeamBoards
{
public Team Team { get; set; }
public List<Board> Boards { get; set; }
}
}
Now this is my controller. This is the most significant difference from the solution in the link referenced above. I build out the ViewModel to send to the view differently.
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);
}
Then in my view I do not specify it as a list. I just do "@model TaskBoard.Models.ViewModels.TeamBoards" Then I only need a for each when I iterate over the Team's boards. Here is my view:
@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>
I am fairly new to ASP.NET MVC so it took me a little while to figure this out. So, I hope this post helps someone figure it out for their project in a shorter timeframe. :-)
Create one new class in your model and properties of LoginViewModel
and RegisterViewModel
:
public class UserDefinedModel()
{
property a1 as LoginViewModel
property a2 as RegisterViewModel
}
Then use UserDefinedModel
in your view.
This is a simplified example with IEnumerable.
I was using two models on the view: a form with search criteria (SearchParams model), and a grid for results, and I struggled with how to add the IEnumerable model and the other model on the same view. Here is what I came up with, hope this helps someone:
@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; }
}
}
Add this ModelCollection.cs to your Models
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)];
}
}
}
Controller:
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);
}
}
The View:
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
you can always pass the second object in a ViewBag or View Data.
with your BigViewModel you do:
@model BigViewModel
@using(Html.BeginForm()) {
@Html.EditorFor(o => o.LoginViewModel.Email)
...
}
you can create 2 additional views
Login.cshtml
@model ViewModel.LoginViewModel
@using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
@Html.TextBoxFor(model => model.Email)
@Html.PasswordFor(model => model.Password)
}
and register.cshtml same thing
after creation you have to render them in the main view and pass them the viewmodel/viewdata
so it could be like this:
@{Html.RenderPartial("login", ViewBag.Login);}
@{Html.RenderPartial("register", ViewBag.Register);}
or
@{Html.RenderPartial("login", Model.LoginViewModel)}
@{Html.RenderPartial("register", Model.RegisterViewModel)}
using ajax parts of your web-site become more independent
iframes
, but probably this is not the case