MVC5 why include not exists property when submit form? - c#

There is My Admin Model:
[Required]
public int AdminId { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string Name {get; set;}
I need user Login (not register, so I don't need Name property):
So , there is 2 property:
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.EditorFor(model => model.AdminId)
#Html.EditorFor(model => model.Password)
This is the Action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "AdminId, Password", Exclude = "Name")]Admin admin)
{
if (ModelState.IsValid)
{
//
}
}
But The ModelState.IsValid Always return false, tell me Name property not exists.
I know I use [Required], but it's only for register, not for login, I even not add Name property when Login. Why still return false? How Can I fixed it?
Update 1
I just add a another new property:
[Required]
public int WorkPlace { get; set; }
But It not be valid by ModelState.IsValid......but Why Name Need?? because of string type?
Update 2
Looks like It's a some kind of BUG. because the int type can remove
use Exclude. but the string type can't ......

Its not a bug. Using the Bind.Exclude just means even if the property value is posted back, don't set its value in my model. Its used to prevent over posting attacks as explained in more detail here. int type works in your case because the default value for int is 0 so its valid, but for string the default is null so [Required] fails. The best way to handle this is to create a view model that contains only the properties you want to edit
public class LogInVM
{
[Required]
public int AdminId { get; set; }
[Required]
public string Password { get; set; }
}
and then post back that model (no Bind attribute is required)
public ActionResult Index(LogInVM model)
{
....

Your admin object is just a parameter. But your Admin View Model requires a Name property. Your model state is thus correctly invalid when the Name property is missing.
Two quick solutions would be :
Remove the Required attribute
Create a new ViewModel for Login
I'd opt for 2
Update
Actually I didn't know this existed, but you can use ModelState.Remove as per this answer

Related

Accessing model value for if statement

I'm new to MVC and I'm trying to make an if statement in the controller that accesses a value from my model. For example, if I am trying to access this radio button value from my model, how would I do it?
[Required] public bool radbutton { get; set; }.
At the top, I tried 'using PasswordTool.Models'.
Then inside my method:
if(PasswordModel.radbutton)
//do something
PasswordModel
namespace PasswordTool.Models
{
public class PasswordModel
{
[Required] public string Password { get; set; }
[Required] public bool RadioButton { get; set; }
}
}
I expect the PasswordModel.radbutton to access the value of the radio button in the model, but intellisense isn't even registering that it exists.
Hi there heisenberg3481,
Welcome to StackOverflow!
To pass Model values from the View you would need to do the following:
#model PasswordModel
// - If the bellow doesn't work try removing the 'Controller' in "MemberController" - //
#using (Html.BeginForm("GetPassword", "MemberController"))
{
Html.TextBoxFor(x => x.Password);
Html.RadioButtonFor(x => x.RadioButton);
<button type="submit">
Submit
</button>
}
Then in your controller you can retrieve the data like so:
[HttpPost]
public ActionResult GetPassword (PasswordModel objModel)
{
if (objModel.RadioButton) {
// Execute action
}
return View();
}

MVC Routing with Period in parameter name

I am trying to implement a remote validation using entity framework in an MVC application. I need help trying to define the action signature and the appropriate route config. I have the following class in my model:
public class FiscalReports
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long id { get; set; }
public Int64 Counter { get; set; }
public short FiscalYear { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MMM dd, yyyy}")]
[DisplayFormat(DataFormatString = "${0:N0}")]
[Remote("ValidateSalary", "FiscalReports", AdditionalFields ="Counter, FiscalYear")]
public int? Salaries { get; set; }
}
I have a viewmodel which is used for a view that contains several of the above objects.
public class FiscalReportVM
{
public FiscalReports CurrentFR { get; set; }
public FiscalReports ReportedToDate { get; set; }
public FiscalReports BudgetToDate { get; set; }
}
The Validation action is in the FiscalReports controller is as follows:
public JsonResult ValidateSalary(int Salaries, short FiscalYear, int Counter)
{
return ValidateFiscalField(Salaries, FiscalYear, Counter, "Salaries");
}
In the view I am using the HTML helper
#Html.EditorFor(model=>model.CurrentFR.Salaries)
This generates the field and validation correctly. Generated HTML is below
input data-val="true" data-val-number="The field Salaries must be a number." data-val-remote="'Salaries'; is invalid." data-val-remote-additionalfields="*.Salaries,*.Counter,*.FiscalYear" data-val-remote-url="/FiscalReports/ValidateSalary" name="CurrentFR.Salaries" type="number" value="" />
The validation request is firing properly and in fiddler I see the following request:
http://localhost:50409/FiscalReports/ValidateSalary?CurrentFR.Salaries=27000&CurrentFR.Counter=4773&CurrentFR.FiscalYear=2
My problem is that I have trouble defining a route and action with the variables in dotted notation. The action definition does not accept dotted parameters (Can't do ValidateSalary(int CurrentFR.Salaries,....). I need help trying to define the action signature and the appropriate route config.
Can't you just use a bit of JQuery to change the name attribute? Something to the effect of:
$("CurrentFR.Salaries").attr('name', 'Salaries')
Remember having a not dissimilar issue and I just temporarily changed the name in the view, and then changed in back in the action.

#Html.Dropdownlist list isn't saving to database

In the below example I'm trying to save the Id from the Asp.Net Identity, "aspnetusers" table "Id" column into my "Application" table. The dropdownlist of "fullname" populates, but is not saving to my "application" table when I submit an application. I need the "Id" from the aspnetusers table to be saved to my "application" table after submitting the application. Help would be greatly appreciated!
Controller:
private ApplicationDbContext db = new ApplicationDbContext();
public ActionResult Index()
{
ViewBag.FullNameList = new SelectList(db.Users,"Id","FullName");
return View();
}
// POST: Applications/Create
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "Id,FullName, FirstName,MiddleInitial,LastName,")] Application application)
{
if (ModelState.IsValid)
{
ViewBag.FullNameList = new SelectList(db.Users, "Id", "FullName", application.ApplicationUser);
db.Applications.Add(application);
db.SaveChanges();
return RedirectToAction("Thanks");
}
}
View:
<p>
#Html.DropDownListFor(m => m.FullName, (SelectList)ViewBag.FullNameList, "Select Loan Officer")
</p>
Model:
public class Application
{
public int ApplicationId { get; set; }
[Required]
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Middle Initial")]
public string MiddleInitial { get; set; }
[Required]
[DisplayName("Last Name")]
public string LastName { get; set; }
public virtual string FullName {get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
Many things wrong:
Your post action accepts Application, which doesn't have a FullName property.
Even if it did, your Bind attribute doesn't included it.
You can't have a ViewBag member holding your select list with the same name as the field you're trying to post. Change it to something like ViewBag.FullNameChoices.
The posted value would be the Id of the "loan officer" user and you're doing nothing with it. If you actually had a foreign key property, you could post directly to that, but instead you're just relying on EF to create an implicit property which you have no access to. In your post action, you would need to look up the user with that Id from the database and then set your ApplicationUser property to that.
While not technically wrong, having a property that represents the "loan officer" for an application call ApplicationUser is not intuitive. You should change it to something like LoanOfficer. Also, it looks like your assuming that all users for all time will also be "loan officers", you should probably plan some flexibility by creating a subclass of ApplicationUser for a loan officer or use roles to assign that status.
Are you forgetting to add an [HttpPost]?
Also, your DropDownList might be:
#Html.DropDownList("FullName", ViewBag.FullName, "Select Loan Officer")

Null values on model after sending model to controller

I'm using .net MVC.
I have some values in a form like:
#Html.TextBoxFor(model => model.Name, new { #class = "form-control" })
On the controller, I get the data like:
public ActionResult NovaPessoa(Person person)
{
The problem is that I just can get values that I have placed the #Html.TextBoxFor markup.
All the other complex information, like person.ContactInformation is lost after submiting and I can't use the SaveChanges in Entity Framework, because it will give me an invalid object after using the Atach method.
The question is: Do I need to use the #Html.TextBoxFor markup for all my model properties, even if I'm not using then to display anything, just to have them on Controller?
You are correct. What people (incorrectly) do normally, is use HiddenFor:
#Html.HiddenFor(model => model.ContactInformation)
What you should be doing, is cutting down your model into a view model with only the appropriate properties.
So, don't use this model:
public class PersonVM {
public int PersonId { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public string ContactInformation { get; set; }
}
..if all you're doing is updating the contact information. Instead, create a new class for your model:
public class PersonContactInfoEditVM {
public int PersonId { get; set; }
public string ContactInformation { get; set; }
}
That's all you need. This saves you from creating invalid objects when you don't add 30 HiddenFor elements to your page .. resulting in very broken data.
You might be thinking "ugggghhhh, all that manual mapping from PersonContactInfoEditVM to Person... I don't want to be writing that sort of code". No one does.. which is why the following libraries exist:
AutoMapper
ValueInjector

ASP.NET MVC Exclude Data Annotation Attribute in Html.ValidationMessageFor

I use the EF-CF, and have the following entities:
Countries, Companies
Companies has a property called CountryId with StringLength attribute and the min and max restrictions (min 3 chars, max 3 chars, country id is ISO-Alpha-3). When the user needs to create a Company, I show a html element with all available countries. This is perfect!
However, when the I execute the jquery validator to the form, this checks for 3 selected options and not the length selected option value.
I need the StringLengthAttribute in my Country Model, I cannot remove it.
I hope to "remove" or "hide" the StringLengthAttribute in the call:
#Html.ValidationMessageFor(model => model.CountryId)
Thanks!
I think I understand your question. A possible solution would be to use a ViewModel to pass to the view as oppose to using the Company entity directly. This would allow you to add or remove data annotations without changing the entity model. Then map the data from the new CompanyViewModel over to the Company entity model to be saved to the database.
For example, the Company entity might look something like this:
public class Company
{
public int Id { get; set; }
[StringLength(25)]
public string Name { get; set; }
public int EmployeeAmount { get; set; }
[StringLength(3, MinimumLength = 3)]
public string CountryId {get; set; }
}
Now in the MVC project a ViewModel can be constructed similar to the Company entity:
public class CompanyViewModel
{
public int Id { get; set; }
[StringLength(25, ErrorMessage="Company name needs to be 25 characters or less!")]
public string Name { get; set; }
public int EmployeeAmount { get; set; }
public string CountryId { get; set; }
}
Using a ViewModel means more view presentation orientated annotations can be added without overloading entities with unnecessary mark-up.
I hope this helps!
Ready!
I remove the rule for the html control.
$("##(Html.HtmlIdNameFor(model => model.CountryId))").rules("remove", "rangelength");
The "rangelength" is the jquery validation rule for the StringLengthAttribute.
Where "Html.HtmlIdNameFor" is a helper to get the "Id" generated by ASP.NET MVC.
Review How to get the HTML id generated by asp.net MVC EditorFor

Categories