I want to have 2 models in one view. The page contains both LoginViewModel
and RegisterViewModel
.
e.g.
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; }
}
Do I need to make another ViewModel which holds these 2 ViewModels?
public BigViewModel
{
public LoginViewModel LoginViewModel{get; set;}
public RegisterViewModel RegisterViewModel {get; set;}
}
I need the validation attributes to be brought forward to the view. This is why I need the ViewModels.
Isn't there another way such as (without the 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)
}
ViewModel is nothing but a single class that may have multiple models. It contains multiple models as a property.
Yes, It is possible to share a view across multiple controllers by putting a view into the shared folder. By doing like this, you can automatically make the view available across multiple controllers.
ViewData is a dictionary of objects that is derived from ViewDataDictionary class and accessible using strings as keys. ViewBag is a dynamic property that takes advantage of the new dynamic features in C# 4.0. ViewData requires typecasting for complex data type and check for null values to avoid error.
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
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.
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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With