ModelState.IsValid keeps coming back as false? - c#

SO I have a view where a member enters the PIN associated with them to clock in
#model Models.Member
#{
Layout = "~/Views/Shared/_HomeLayout.cshtml";
}
<h1 style="margin-top: 0px;">Club Members Login Below!</h1> #*add this to the style for better ipad title -> "text-align: center;"*#
</br>
#using (Html.BeginForm("ClubHours", "Login", FormMethod.Post))
{
#Html.LabelFor(c => c.PIN)
#Html.TextBoxFor(c => c.PIN)<br />
#Html.ValidationMessageFor(c => c.PIN)<br />
<input type="submit" name="submit" value="ClockIn" />
<input type="submit" name="submit" value="ClockOut" />
}
which interacts with this action result:
[HttpPost]
public ActionResult ClubHours(string submit, Member member)//member clocking in
{
if (submit.Equals("ClockIn"))
{
if (!ModelState.IsValid) //validating events fields
{
return View("UserLogin");
}
else
{
var mem = _context.Members.SingleOrDefault(c => c.PIN == member.PIN);
var hours = new MemberClubHours();
hours.ClockIn = DateTime.Now;
mem.Hours.Add(hours);
_context.SaveChanges();
return View("ClockIn");
}
}
else if (submit.Equals("ClockOut"))
{
if (!ModelState.IsValid) //validating events fields
{
return View("UserLogin");
}
else
{
var mem = _context.Members.SingleOrDefault(c => c.PIN == member.PIN);
var hours = new MemberClubHours();
hours.ClockOut = DateTime.Now;
mem.Hours.Add(hours);
_context.SaveChanges();
return View("ClockOut");
}
}
else
{
return View("UserLogin","Login");
}
}
and lastly here is that Member class
public class Member
{
public int Id { get; set; }
[Required]
[MaxLength(4, ErrorMessage = "PIN must be 4 numbers long"), MinLength(4, ErrorMessage = "PIN must be 4 numbers long")]
public string PIN { get; set; }
[Required]
[Display(Name ="First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Date of Birth")]
public DateTime? Birthdate { get; set; }
public virtual ICollection<MemberClubHours> Hours { get; } = new HashSet<MemberClubHours>();
}
and the memberclubhours class
public class MemberClubHours
{
public int Id { get; set; }
public DateTime? ClockIn { get; set; }
public DateTime? ClockOut { get; set; }
[Required]
public Member Member { get; set; }
}
The code works correctly and will track the hours of a member, however I'm trying to implement validation, but even if I enter a PIN that is associated with a member in the system, it comes back as not valid? any help would be appreciated!

You're validating the model sent from the frontend. Not to the database, are you sure the model is populated according to your Data Annotations in the model?
Check out ModelState it's asp.net core 3.1 but still relevant

Member model is coming out as invalid because the member Model requires the Id, PIN, FirstName, LastName fields.
You can't validate this class with ModelState.IsValid because it will check all the properties-- and it looks like you're only passing the PIN property.
If you don't want to make a viewModel for it, you could just include those properties as hidden inputfields;
#using (Html.BeginForm("ClubHours", "Login", FormMethod.Post))
{
#Html.HiddenFor(c=>c.Id)
#Html.HiddenFor(c=>c.FirstName)
#Html.HiddenFor(c=>c.LastName)
#Html.LabelFor(c => c.PIN)
#Html.TextBoxFor(c => c.PIN)<br />
#Html.ValidationMessageFor(c => c.PIN)<br />
<input type="submit" name="submit" value="ClockIn" />
<input type="submit" name="submit" value="ClockOut" />
}

Related

Why am I getting the 'field is required' validation error even though the field is passed with the form?

I am writing a .NET Core 3.0 MVC Web app. I have a JobApplication model that looks like this:
public class JobApplication
{
[Key]
public int Id{ get; set; }
[Required]
public DateTime CreatedOn { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyy-MM-dd}")]
[Display(Name = "Edited on:")]
public DateTime? EditedOn { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyy-MM-dd}")]
[Display(Name = "Deleted on:")]
public DateTime? DeletedOn { get; set; }
[Required]
public User Applicant { get; set; }
[Required]
public JobOffer JobOffer { get; set; }
[Required]
public ApplicationStatus ApplicationStatus { get; set; }
public string CvHandle { get; set; }
public string AdditionalInformation { get; set; }
}
As you can see, the model holds references to the Job Offer it was created for and the applicant that created the application.
I also have a Controller JobApplicationController that has 2 Create methods:
public async Task<ActionResult> Create(int? id)
{
var offer = await _context.JobOffers.Include(x => x.CreatedFor).FirstOrDefaultAsync(x => x.Id == id.Value);
var user = await _context.Users.FirstOrDefaultAsync(x => x.Name == "Filip");
var model = new JobApplication()
{
JobOffer = offer,
Applicant = user
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([FromForm]JobApplication model)
{
if (!ModelState.IsValid)
{
return View(model);
}
JobApplication ja = new JobApplication
{
...
};
await _context.JobApplications.AddAsync(ja);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
As you can see, one of them returns the Create view, the other gets the model from the view and adds it to the database. I also emphasize that in the first method, the 'JobOffer' and 'Applicant' fields are taken from the database and passed to the form with a model. Here's how the view is set up:
#model HRpest.BL.Model.JobApplication
#{
ViewData["Title"] = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
#Html.Hidden("Offer", Model.JobOffer)
#Html.Hidden("Applicant", Model.Applicant)
<div asp-validation-summary="None" class="text-danger"></div>
<div class="form-group">
<label asp-for="CvHandle" class="control-label"></label>
<input asp-for="CvHandle" class="form-control" />
<span asp-validation-for="CvHandle" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AdditionalInformation" class="control-label"></label>
<input asp-for="AdditionalInformation" class="form-control" />
<span asp-validation-for="AdditionalInformation" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
It all seems fine. However, when I try to add an application and submit the form, I get an error:
{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"One or more validation errors occurred.","status":400,"traceId":"|a72c03fc-4f6501d7781e4a9a.","errors":{"JobOffer":["The JobOffer field is required."],"Applicant":["The Applicant field is required."]}}
I don't understand it. I have both fields in my model. How to make this go away?
Thank you so much in advance.
You need to use HiddenFor() Instead of Hidden().
Read here to understand the difference between the two.
I think is because required too. Because navigation property is complex object and should not be required
public virtual User Applicant { get; set; }
public virtual JobOffer JobOffer { get; set; }
public virtual ApplicationStatus ApplicationStatus { get; set; }
In the view, you are saying, #Html.Hidden("Offer", Model.JobOffer). It should instead be #Html.Hidden("JobOffer", Model.JobOffer) because that's the property name. You wouldn't make that mistake if you were using HiddenFor.
On a more abstract level, you are binding directly to the database entity. It's never a good idea. You should use a model to get the posted values and then copy those values to the entity. You can use AutoMapper to do that automatically for you.

ASP.NET model binding breaks when using decimal values

I have the following scenario. I have a form that post to a controller. Everything works fine when I enter a non decimal number on ValorKilometro input. I can get the model perfectly on the controller and so. The thing is that when I enter a decimal value the ValorKilometro property is always set to 0. Why is that?. Here is the code:
<form name="theForm" action="" style="margin: 0 auto; width: 80%;" method="post" onsubmit="return onFormSubmit();">
...
<div class="form-group">
<label for="usr">Valor de Kilometro:</label>
<input type="number" name="ValorKilometro" min="0" step="any" class="form-control" value="#Model.ValorKilometro">
</div>
<button type="submit" id="boton" class="btn btn-success">Guardar</button>
</form>
Model:
public class ConfiguracionModel
{
public Guid EmpresaGuid { get; set; }
public bool MaximoHabilitado { get; set; }
public int MontoMaximo { get; set; }
public Guid Moneda { get; set; }
public Double ValorKilometro { get; set; }
}
Controller:
[Authorize, AdminAuthorization]
[HttpPost]
public ActionResult Configuracion(ConfiguracionModel configuracion)
{
configuracion.EmpresaGuid = SQL.GetEmpresaGuid(User.Identity.Name);
SQL.ModificarConfiguracion(configuracion);
TempData["msg"] = "<script>alert('Los cambios fueron guardados correctamente!.');</script>";
return View(configuracion);
}
I hope someone can help me out with this.Thanks.
Have you tried using #Html.TextBoxFor helper?
#model ConfiguracionModel // <-- obviously you need to bind your View to your model
#Html.TextBoxFor(m => m.ValorKilometro, "{0:n2}", new {
#class = "form-control",
#type = "number",
#min = "0" })
You can also add the validation constraint to your model:
public class ConfiguracionModel
{
public Guid EmpresaGuid { get; set; }
public bool MaximoHabilitado { get; set; }
public int MontoMaximo { get; set; }
public Guid Moneda { get; set; }
[Range(0.0, double.MaxValue)]
[DisplayFormat(DataFormatString = "{0:n2}", ApplyFormatInEditMode = true)]
public Double ValorKilometro { get; set; }
}
Note that {0:n2} indicates 2 decimal places.

Updating AspNetUsers with extra properties won't work

Goodevening. I am stuck on something I can't seem to fix my self. I created a razor page called Address.cshtml.cs (model) and Address.cshtml (view) in my project for users to be able to add their user information AFTER registering. And var result = await _userManager.UpdateAsync(user); doesn't seem to work for it. I tried two ways to update it in the database:
First try
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AccountChange(UserModel model)
{
if (ModelState.IsValid)
{
// Get the current application user
var user = await _userManager.GetUserAsync(User);
//Update the details
user.name = model.name;
user.surname = model.surname;
user.street = model.street;
user.streetnumber = model.streetnumber;
user.city = model.city;
user.zipcode = model.zipcode;
// Update user address
var result = await _userManager.UpdateAsync(user);
}
//await _signInManager.RefreshSignInAsync(User);
_logger.LogInformation("User added their address information successfully.");
StatusMessage = "Your address information has been added.";
return RedirectToPage();
}
}
Second try
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(UserModel model)
{
if (ModelState.IsValid)
{
UserModel u = await _userManager.FindByIdAsync(model.Id);
u.name = model.name;
u.surname = model.surname;
u.street = model.street;
u.streetnumber = model.streetnumber;
u.city = model.city;
u.zipcode = model.zipcode;
await _userManager.UpdateAsync(u);
return RedirectToAction("Index");
}
return RedirectToPage();
}
How can I fix this? I've added all of the necessary code below.
Address.cshtml.cs
namespace bytme.Areas.Identity.Pages.Account.Manage
{
public class AddressModel : PageModel
{
private readonly UserManager<UserModel> _userManager;
private readonly SignInManager<UserModel> _signInManager;
private readonly ILogger<AddressModel> _logger;
public AddressModel(
UserManager<UserModel> userManager,
SignInManager<UserModel> signInManager,
ILogger<AddressModel> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}
[BindProperty]
public InputModel Input { get; set; }
public string ReturnUrl { get; set; }
[TempData]
public string StatusMessage { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Name")]
[StringLength(100, ErrorMessage = "Invalid input. Maximum is 100 characters.")]
public string name { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Surname")]
[StringLength(100, ErrorMessage = "Invalid input. Maximum is 100 characters.")]
public string surname { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Street")]
[StringLength(48, ErrorMessage = "The longest street name in the Netherlands is 48 characters.")]
public string street { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "House Number")]
[StringLength(5, ErrorMessage = "The longest house number in the Netherlands is 5 characters.")]
public string streetnumber { get; set; }
//[DataType(DataType.Text)]
//[Display(Name = "House Number Addition", Description = "For example A or II")]
//[StringLength(6, ErrorMessage = "
//public string streetnumberadd { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "City")]
[StringLength(28, ErrorMessage = "The longest place name in the Netherlands is 28 characters.")]
public string city { get; set; }
[Required]
[DataType(DataType.PostalCode)]
[Display(Name = "Postal Code")]
[RegularExpression(#"^[1-9][0-9]{3}\s?[a-zA-Z]{2}$", ErrorMessage = "Invalid zip, for example: 1234AB")]
public string zipcode { get; set; }
}
public void OnGet(string returnUrl = null)
{
ReturnUrl = returnUrl;
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AccountChange(UserModel model)
{
if (ModelState.IsValid)
{
// Get the current application user
var user = await _userManager.GetUserAsync(User);
//Update the details
user.name = model.name;
user.surname = model.surname;
user.street = model.street;
user.streetnumber = model.streetnumber;
user.city = model.city;
user.zipcode = model.zipcode;
// Update user address
var result = await _userManager.UpdateAsync(user);
}
//await _signInManager.RefreshSignInAsync(User);
_logger.LogInformation("User added their address information successfully.");
StatusMessage = "Your address information has been added.";
return RedirectToPage();
}
}
}
Address.cshtml
#page
#model AddressModel
#inject SignInManager<UserModel> SignInManager
#using Microsoft.AspNetCore.Identity
#using bytme.Models;
#{
ViewData["Title"] = "Add Address Information";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#Html.Partial("_StatusMessage", Model.StatusMessage)
#{
var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any();
}
<div>
<h3>Change your account settings</h3>
<hr />
<div class="row">
<div class="col-md-3">
<partial name="_ManageNav" />
</div>
<div class="col-md-9">
<div class="row">
<div class="col-md-6">
<h4>#ViewData["Title"]</h4>
<form id="change-password-form" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.name"></label>
<input asp-for="Input.name" class="form-control" />
<span asp-validation-for="Input.name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.surname"></label>
<input asp-for="Input.surname" class="form-control" />
<span asp-validation-for="Input.surname" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.street"></label>
<input asp-for="Input.street" class="form-control" />
<span asp-validation-for="Input.street" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.streetnumber"></label>
<input asp-for="Input.streetnumber" class="form-control" />
<span asp-validation-for="Input.streetnumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.city"></label>
<input asp-for="Input.city" class="form-control" />
<span asp-validation-for="Input.city" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.zipcode"></label>
<input asp-for="Input.zipcode" class="form-control" />
<span asp-validation-for="Input.zipcode" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</div>
</div>
</div>
#section Scripts {
<partial name="_ValidationScriptsPartial" />
}
UserModel.cs
namespace bytme.Models
{
public class UserModel : IdentityUser
{
public override string Id { get; set; }
public override string Email { get; set; }
public override string UserName { get; set; }
public override string PasswordHash { get; set; }
public string zipcode { get; set; }
public string city { get; set; }
public string street { get; set; }
public string streetnumber { get; set; }
public string name { get; set; }
public string surname { get; set; }
}
}
ApplicationDbContext.cs
namespace bytme.Data
{
public class ApplicationDbContext : IdentityDbContext<UserModel>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
public DbSet<bytme.Models.Item> Items { get; set; }
public DbSet<bytme.Models.ItemCategories> ItemCategories { get; set; }
public DbSet<bytme.Models.UserModel> UserModels { get; set; }
public DbSet<bytme.Models.OrderHistory> OrderHistories { get; set; }
public DbSet<bytme.Models.OrderMain> OrderMains { get; set; }
public DbSet<bytme.Models.OrderStatus> OrderStatuses { get; set; }
public DbSet<bytme.Models.WishlistModel> WishlistModels { get; set; }
public DbSet<bytme.Models.ShoppingCartModel> ShoppingCartModels { get; set; }
}
}
After checking the code, I found several problems.
1. I have doubts about your post method AccountChange. The following conventions should be followed. According to Microsoft docs about razor pages, there are generated several default method handlers, like: OnGet OnPost OnGetAsync OnPostAsync etc. However, if you want to use custom handler name, it has to follow some naming convention as well.
Method should start with
OnPost[Get,...]<Handlername>[Async|NoAsync](its optional).
So your method shoud be named like OnPostAccountChangeAsync()
If you define such a method, you has to tell a view, that you want to use the specified handler. You tell it with asp-page-handler directive, so your form shoud looks like this:
<form id="change-password-form" method="post">
//...
<button type="submit" class="btn btn-default" asp-page-handler="AccountChange">Submit</button>
</form>
2. You are binding your property Input
[BindProperty]
public InputModel Input { get; set; }
However, in your method OnPostAccountChangeAsync(), you are trying to accces in the parameter instance of type UserModel, which gonna be null. Its not binded. In the good case you will get a null exception. In bad one, you will update your entity with null properties. So your method OnPostAccountChangeAsync() should accept at parameter instance of InputModel or you can access this property directly inside the body of method (you can get rid of parameter)
OnPostAccountChangeAsync(InputModel Input)
{
//...
}
3. Its minor thing, however, it improves the readability of your code. Please be consistent in naming of your variables. Consitency makes better orientation in your code. In Csharp properties should start with a capital letter (PascalCasing). Do not hesitate, and check C# naming convention
To your question about UserManager in comment section:
You can look at Identity as wrapper (rather say api), which provides useful methods for user management, besides that it offer other things. As I said, if you look at your package dependecies, you can spot that Entity Framework is shipped within Identity nuget package. What does it mean? It opens you possibility to not be only dependent on some "mysterous" Identity. You can clearly use Entity Framework for e.g. saving new user into table AspNetUsers. Clearly said, you are not limited. However, if you already use Identity, it is better to use its available methods.

MVC Add Record to Database

Could somebody please help me add a record to a database?
I have created some base elements, but I'm struggling with the code for the AccountController. I would like for a user to enter the values for Stone and Pound via the form, and on posting add a record to the Weight table along with current Id of the logged in user and current date. Here is what I have so far.
AddWeightModel
public class AddWeightModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Stone")]
public Nullable<short> Stone { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Pound")]
public Nullable<short> Pound { get; set; }
}
WebApplication1Entities
public partial class WebApplication1Entities : DbContext
{
public WebApplication1Entities()
: base("name=WebApplication1Entities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Weight> Weights { get; set; }
}
Weight
public partial class Weight
{
public int Id { get; set; }
public string UserId { get; set; }
public Nullable<short> Stone { get; set; }
public Nullable<short> Pound { get; set; }
public Nullable<System.DateTime> Date { get; set; }
}
_UpdatePartial
#using Microsoft.AspNet.Identity
#model WebApplication1.Models.AddWeightModel
#using (Html.BeginForm("RecordCard", "Account", FormMethod.Post, new { #id = "contact-form", role = "form" }))
{
<fieldset>
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="form-div-5">
<label>
#Html.TextBoxFor(m => m.Stone, new { #placeholder = "Stone *", #type = "text" })
</label>
</div>
<div class="form-div-5">
<label>
#Html.TextBoxFor(m => m.Pound, new { #placeholder = "Pound *", #type = "text" })
</label>
</div>
<div class="button-wrapper">
<input type="submit" value="Submit" class="button" />
</div>
</fieldset>
}
AccountController
public ActionResult RecordCard()
{
var UserId = User.Identity.GetUserId();
var weightModel = from m in db.Weights where m.UserId == UserId select m;
return View(weightModel);
}
public ActionResult RecordCard(Weight Model)
{
if (ModelState.IsValid)
{
using (WebApplication1 db = new WebApplication1())
{
Weight weight = new Weight();
weight.UserId = User.Identity.GetUserId();
weight.Stone = Model.Stone;
weight.Pound = Model.Pound;
weight.Date = System.DateTime.Now;
db.Weights.Add(Model);
db.SaveChanges();
}
}
return View(Model);
}
Please note that _UpdatePartial is called from RecordCard like so:
#Html.Partial("_WeightPartial", new AddWeightModel())
And also RecordCard receives an IEnumerable list:
#model IEnumerable<Shedtember.Models.Weight>
I need a list of records from the Weight table depending on logged in user to generate a graph.
Just want to add the record and return to the RecordCard page.
Please help, I'm going insane!
I'll try to break this down.
Your compile error at:
db.Weights.Add(Model);
occurs because db.Weights.Add() expects a Weight. You are passing it your model of type AddWeightModel. You need to convert your model back to a Weight:
Weight weight = new Weight();
weight.UserId = //get your current user's ID
weight.Stone = Model.Stone;
weight.Pount = Model.Pound;
weight.Date = DateTime.UTCNow;
db.Weights.Add(weight);
Next, your method
public ActionResult RecordCard(AddWeightModel Model)
needs to be a POST, so decorate it:
[HttpPost]
public ActionResult RecordCard(AddWeightModel Model)
Now in your view you are (very correctly) adding a #Html.AntiForgeryToken(). It doesn't help you unless you validate it:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult RecordCard(AddWeightModel Model)
Step back, and examine what types you are working with.

Validate fields based on RadioButton (MVC)

I have got some sign up form that has 2 otpions
Personal
Company
Some of the fields for them are the same like Password, Username, Email and other are different.
My question is which strategy of the MODEL we should implement in that case?
Have we i.e. use 2 "TABs" and 1 button "SUBMIT" but in that case we have duplicated UI fields... And I don't understand how the class MODEL should be in that case and how we have to validate it...
Or...
We need to use 2 buttons "SUBMIT" and use somehow 2 MODELS ....
I know we can use if (code is following) but which MODEL we have need for it?
<html>
...
<body>
<div>
<form action="/SignUp/Personal" method="post">
<input type="text" name="username" value="" />
<input type="text" name="passowrd" value="" />
<input type="submit" name="signup" value="SUBMIT" />
</form>
<form action="/SignUp/Company" method="post">
<input type="text" name="username" value="" />
<input type="text" name="passowrd" value="" />
<input type="submit" name="signup" value="SUBMIT" />
</form>
</div>
</body>
</html>
Well I don't know which approach we can use...
Any clue?
Thank you!!!
There is several approaches, but exists approach which which allow you don't duplicate UI fields and have single submit button, you can divide your model validation depending on selected user AccountType, custom ActionMethodSelectorAttribute help you to divide methods depending on user account type. Model will be automatically validated in appropriative action.
Here is sample implementation:
controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new SignUpModel
{
Common = new Common(),
Personal = new Personal(),
Company = new Company()
});
}
[HttpPost]
[SignUpSelector]
[ActionName("Index")]
public ActionResult PersonalRegistration(Personal personal, Common common)
{
if (ModelState.IsValid)
{
//your code
}
return View("Index", new SignUpModel()
{
Common = common,
Personal = personal,
Company = new Company()
});
}
[HttpPost]
[SignUpSelector]
[ActionName("Index")]
public ActionResult CompanyRegistration(Company company, Common common)
{
if(ModelState.IsValid)
{
//your code
}
return View("Index", new SignUpModel()
{
Common = common,
Personal = new Personal(),
Company = company
});
}
}
model:
public class SignUpModel
{
public string AccountType { get; set; }
public Common Common { get; set; }
public Company Company { get; set; }
public Personal Personal { get; set; }
}
public class Company
{
[Required]
public string CompanyName { get; set; }
public string Address { get; set; }
}
public class Personal
{
[Required]
public string FirstName { get; set; }
public int Age { get; set; }
}
public class Common
{
[Required]
public string UserName { get; set; }
[Required]
public string Passwrod { get; set; }
}
custom ActionMethodSelectorAttribute:
public class SignUpSelector : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
return (controllerContext.HttpContext.Request.Form["AccountType"] + "Registration" == methodInfo.Name);
}
}
view:
#model MvcModelValidationTest.Controllers.SignUpModel
#{
ViewBag.Title = "Home Page";
}
#using(Html.BeginForm())
{
#Html.Display("Personal")
#Html.RadioButtonFor(x=>x.AccountType, "Personal",new { #checked = "checked" })<br/>
#Html.Display("Company")
#Html.RadioButtonFor(x=>x.AccountType, "Company")<br/>
#Html.TextBoxFor(x=>x.Common.UserName)<br/>
#Html.PasswordFor(x=>x.Common.Passwrod)<br/>
#Html.TextBoxFor(x=>x.Company.CompanyName)<br/>
#Html.TextBoxFor(x=>x.Company.Address)<br/>
#Html.TextBoxFor(x=>x.Personal.FirstName)<br/>
#Html.TextBoxFor(x=>x.Personal.Age)<br/>
<input type="submit"/>
}

Categories

Resources