Reputation: 48686
I have a single page on my MVC application. On this page, a user can enter their user name and password and click a login button, or the user can enter their first name, email address and click a signup button.
My initial thought was to create a ViewModel with UserName
, Password
, FirstName
and Email
properties, all with a [Required]
attribute. Then having a view with two Html.BeginForm()
's. Although this would probably work, I have the feeling that when I post my data back to my controller, ModelState.IsValid
will always return false
since the ViewModel, is indeed, invalid.
So can someone tell me the proper way to handle a situation like this?
Upvotes: 0
Views: 562
Reputation: 609
If you want to try different library then you can go with FluentValidation
FluentValidation provides elegant way to customize validation for single class for different methods.
For e.g
[Validator(typeof(LoginRegisterModelValidator))]
public class LoginRegisterViewModel
{
public string UserName { get; set; }
public string Password { get; set; }
public string FirstName{ get; set; }
public string Email { get; set; }
}
You can have multiple rules defined for different actions The validator class would look like this
public class LoginRegisterModelValidator : AbstractValidator<LoginRegisterViewModel>
{
public RegistryAddEditModelValidator()
{
/* Define the rule set to call them specifically inside contrller action parameter with CustomizeValidator Attribute */
RuleSet("LoginRuleSet", LoginRuleSet);
RuleSet("RegisterRuleSet", RegisterRuleSet);
}
protected void LoginRuleSet()
{
RuleFor(x => x.UserName).NotEmpty();
RuleFor(x => x.Password).NotEmpty();
}
protected void RegisterRuleSet()
{
RuleFor(x => x.Email).NotEmpty();
RuleFor(x => x.FirstName).NotEmpty();
}
}
The Controller Action would look like this
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login([CustomizeValidator(RuleSet = "LoginRuleSet")] LoginRegisterViewModel model)
{ ...
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register([CustomizeValidator(RuleSet = "RegisterRuleSet")] LoginRegisterViewModel model)
{ ...
}
}
Hope this helps you to validate different rule with same class.
Upvotes: 1
Reputation: 168
I have implemented very similiar scenario in my project i think the best way of achievie this is to create a viewmodel that will have 2 child viewmodel's inside something like this:
public class AuthModelView
{
public MemberLoginViewModel LoginModel { get; set; }
public MemberRegisterViewModel RegisterModel { get; set; }
[HiddenInput]
public string ReturnUrl { get; set; }
}
MemberLoginViewModel:
public class MemberLoginViewModel
{
[Required(ErrorMessage = "")]
[Display(Name = "")]
[EmailAddress]
public string Email { get; set; }
[DataType(DataType.Password)]
[Display(Name = "")]
[Required(ErrorMessage = "")]
public string Password { get; set; }
[Display(Name = "")]
public bool RememberMe { get; set; }
}
MemberRegisterViewModel:
public class MemberRegisterViewModel
{
[Required(ErrorMessage = "")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required(ErrorMessage = "")]
[DataType(DataType.Password)]
[Display(Name = "")]
public string Password { get; set; }
[Required]
public string RepeatPassword { get; set; }
[HiddenInput(DisplayValue = false)]
public string ReturnUrl { get; set; }
}
Then you create the View that will render 2 partial views
@Html.Partial("MemberLoginSummary", Model)
@Html.Partial("MemberRegisterSummary", Model)
Where the "Model" is your parent Viewmodel, then you will have 2 separated forms in one view. In your partial view you simply do something like :
@Html.TextBoxFor(m => m.LoginModel.Email, null, new { @class = "form-control", placeholder = "email", id="Email" })
Upvotes: 2
Reputation: 1
You can define 2 view models.
Login view model:
public class LoginViewModel
{
[Requried]
public string UserName { get; set; }
[Requried]
public string Password { get; set; }
}
Register view model:
public class RegisterViewModel
{
[Requried]
public string UserName { get; set; }
[Requried]
public string Password { get; set; }
[Requried]
public string FirstName{ get; set; }
[Requried]
public string Email { get; set; }
}
Login view:
@Html.BeginForm("Login", "Account", FormMethod.Post)
{
<!-- login form implements... -->
}
Register view:
@Html.BeginForm("Register", "Account", FormMethod.Post)
{
<!-- register form implements... -->
}
Controller:
public IActionResult Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
}
}
public IActionResult Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
}
}
Or, if you want only 1 model. Try to remove [Required]
attribute. You can check it inside the action. Like this:
public IActionResult Login(YourViewModel model)
{
if (!string.IsNullOrEmpty(model.UserName) && !string.IsNullOrEmpty(model.Password))
{
}
}
public IActionResult Register(YourViewModel model)
{
if (!string.IsNullOrEmpty(model.UserName) &&
!string.IsNullOrEmpty(model.Password) &&
!string.IsNullOrEmpty(model.FirstName) &&
!string.IsNullOrEmpty(model.Email))
{
}
}
Hope this help!
Upvotes: 1
Reputation: 1423
I believe the proper way is to have a ViewModel for each. Your underlying business logic and/or code talking to the database can still work with a single model if you want.
Your options are discussed well here.
Although I would add that having more than one ViewModel on an MVC page is a pain. Some guidance here on how.
Upvotes: 0