I want to have 2 models in one view. The page contains both LoginViewModel
and RegisterViewModel
.
e.g.
pub
Use a view model that contains multiple view models:
namespace MyProject.Web.ViewModels
{
public class UserViewModel
{
public UserDto User { get; set; }
public ProductDto Product { get; set; }
public AddressDto Address { get; set; }
}
}
In your view:
@model MyProject.Web.ViewModels.UserViewModel
@Html.LabelFor(model => model.User.UserName)
@Html.LabelFor(model => model.Product.ProductName)
@Html.LabelFor(model => model.Address.StreetName)
Do I need to make another view which holds these 2 views?
Isn't there another way such as (without the BigViewModel):
Yes, you can use Tuple (brings magic in view having multiple model).
Code:
@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)
}
I'd recommend using Html.RenderAction
and PartialViewResults to accomplish this; it will allow you to display the same data, but each partial view would still have a single view model and removes the need for a BigViewModel
So your view contain something like the following:
@Html.RenderAction("Login")
@Html.RenderAction("Register")
Where Login
& Register
are both actions in your controller defined like the following:
public PartialViewResult Login( )
{
return PartialView( "Login", new LoginViewModel() );
}
public PartialViewResult Register( )
{
return PartialView( "Register", new RegisterViewModel() );
}
The Login
& Register
would then be user controls residing in either the current View folder, or in the Shared folder and would like something like this:
/Views/Shared/Login.cshtml: (or /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: (or /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)
}
And there you have a single controller action, view and view file for each action with each totally distinct and not reliant upon one another for anything.
My advice is to make a big view model:
public BigViewModel
{
public LoginViewModel LoginViewModel{get; set;}
public RegisterViewModel RegisterViewModel {get; set;}
}
In your Index.cshtml, if for example you have 2 partials:
@addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers
@model .BigViewModel
@await Html.PartialAsync("_LoginViewPartial", Model.LoginViewModel)
@await Html.PartialAsync("_RegisterViewPartial ", Model.RegisterViewModel )
and in controller:
model=new BigViewModel();
model.LoginViewModel=new LoginViewModel();
model.RegisterViewModel=new RegisterViewModel();
Another way is to use:
@model Tuple<LoginViewModel,RegisterViewModel>
I have explained how to use this method both in the view and controller for another example: Two models in one view in ASP MVC 3
In your case you could implement it using the following code:
In the view:
@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"})
}
Note that I have manually changed the Name attributes for each property when building the form. This needs to be done, otherwise it wouldn't get properly mapped to the method's parameter of type model when values are sent to the associated method for processing. I would suggest using separate methods to process these forms separately, for this example I used Login1 and Login2 methods. Login1 method requires to have a parameter of type RegisterViewModel and Login2 requires a parameter of type LoginViewModel.
if an actionlink is required you can use:
@Html.ActionLink("Edit", "Edit", new { id=Model.Item1.Id })
in the controller's method for the view, a variable of type Tuple needs to be created and then passed to the view.
Example:
public ActionResult Details()
{
var tuple = new Tuple<LoginViewModel, RegisterViewModel>(new LoginViewModel(),new RegisterViewModel());
return View(tuple);
}
or you can fill the two instances of LoginViewModel and RegisterViewModel with values and then pass it to the view.
a simple way to do that
we can call all model first
@using project.Models
then send your model with viewbag
// for list
ViewBag.Name = db.YourModel.ToList();
// for one
ViewBag.Name = db.YourModel.Find(id);
and in view
// for list
List<YourModel> Name = (List<YourModel>)ViewBag.Name ;
//for one
YourModel Name = (YourModel)ViewBag.Name ;
then easily use this like Model