MVC: post, catching form values into property of page model - asp.net-mvc

Alright...this may be a bit backwards but, I only need to do it in one spot.
I have a Model
public class LoginModel : xxx.Models.PageVars
{
public Item.LoginAttempt LoginAttempt { get; set; }
public LoginModel()
{
// does a bunch of stuff here...mainly to set the layout properties from PageVar
this.LoginAttempt = new Item.LoginAttempt();
}
}
Login Attempt is a simple obj (for now)
// login attempt
public class LoginAttempt
{
public string Email { get; set; }
public string Password { get; set; }
}
My controller
public ActionResult Login()
{
return View("Login", new Models.LoginModel());
}
[HttpPost]
public ActionResult LoginAttempt(LoginAttempt model)
{
return View("Login", model);
}
In my view
#model xxx.Models.LoginModel
Is there a way to use the property of the obj/model from LoginModel for the #model.
I can get the values from FormCollection or request but...that's not optimal.
thoughts???
tnx

The model for your GET should match the model for your POST. Otherwise, you're not playing on the same field. In order to allow the binding of data from a POST to a model, the HTML Helpers will generate a name that matches the access path of the property in the view's model. In other words, in your form, based on the model being LoginModel, your field names will be LoginAttempt.Email and LoginAttempt.Password. But, in the POST action, you're accepting just LoginAttempt, so the modelbinder is expecting to see data for Email and Password, which it won't find.
There's actually not even any need for this nested class. Just put your Email and Password fields directly on LoginModel and use that for both your view and your POST parameter. Then, you won't have any issues because everything will match up.

Why don't you have the form post controller action accept the parent model LoginModel instead of LoginAttempt? That way, the default MVC model binding should automatically parse the submitted values into the LoginModel and you'll have acces to LoginAttempt.
If it isn't then your form needs to use the prefix values in the names of the properties on the form. This is done automatically when you use TextboxFor, DropdownListFor etc.
In your example, the names of the form fields should start with LoginAttempt.Email etc

I've seen it work 2 ways. First way would be to rename your LoginAttempt model parameter to be
[HttpPost]
public ActionResult LoginAttempt(LoginAttempt loginModel)
{
return View("Login", model);
}
But i would use the Bind(Prefix) option
[HttpPost]
public ActionResult LoginAttempt([Bind(Prefix="LoginModel")] LoginAttempt model)
{
return View("Login", model);
}
you can't really return model of type LoginAttempt to the view though so you'd have to do even more work to get it to work if you're set on doing it this way. You should probably be redirecting to a different page instead of returning the Login view if it succeeds. Other wise return new LoginModel() {LoginAttempt = model}

Related

Asp MVC 5 - ModelState is Invalid?

I have the following view model:
public class CreateCaseViewModel
{
[Required]
public string Subject { get; set; }
[Required]
[DisplayName("Post Content")]
[UIHint("ForumEditor"), AllowHtml]
[DataType(DataType.MultilineText)]
public string PostContent { get; set; }
// some other dropdown properties
}
The following controller action:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create(CreateCaseViewModel viewModel, FormCollection collection)
{
// Re-populate dropdowns
viewModel.Categories = _unitOfWork.CategoryRepository.GetCategories();
viewModel.Subject = collection["Subject"];
viewModel.PostContent = collection["Description"];
try
{
if (ModelState.IsValid)
{
// Do stuff
}
}
catch (DataException dex )
{
throw new ApplicationException("Something :", dex);
}
return View(viewModel);
}
I am manually assigning the value to PostContent from a value in FormCollection as you can see from code above. However I still keep getting modelstate is invalid - I'm returned back to the view with the validation error saying `The Post Content field is required'
Why is modelstate invalid?
When you submit the form the model binder will read the posted request data and map it to your method parameter. After that model validation framework will do the validation. It does not look at your FormCollection for doing this. So in your case, your model validation is failing because as per your view model it is expecting a value for PostContent property and it is not available there. Your action method code where you are setting the value of it gets executed later ( by this time model validation already occurred).
Your options are, either standardize the input element name with your view model property name (rename the PostContent to Description or vice versa)
public class CreateCaseViewModel
{
[Required]
public string Subject { get; set; }
[Required]
[DisplayName("Post Content")]
[UIHint("ForumEditor"), AllowHtml]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
Now let the model binder maps the request body to your view model parameter. Remove the manual assignment from the FormCollection in your action method
Or you can probably create a new custom model binder which does the custom mapping for you (same as what you did in your action method).
I would go with option one. Let the default model binder takes care of it.
The model is validated before it is passed to your controller action. Modifying the model does not change that.
You need to call ModelState.Clear() followed by Controller.TryValidateModel(model) to re-validate the model and reset the IsValid property.

MVC Model Binding Post Values Best Practice

I'm trying to figure out if what I'm doing is flawed or acceptable. Specifically, I'm questioning the NULL value I'm getting back in the POST to Controller in 'Timeframes' property. The 'Timeframe' (singular) property DOES contain the value so all is good. However, is this just how model binding works and the property (Timeframes) that is used to populate the DDL comes back as null? Is this best practice and what I'm doing is fine? Is this a concern of sending values around that are not needed...performance concern?
Timeframe = used to return value back to Controller on Post
Timeframes = used to populate DDL values
Drop Down List Box on View:
#Html.DropDownListFor(m => m.Timeframe, Model.Timeframes)
Model:
public class ABCModel
{
public List<SelectListItem> Timeframes { get; set; }
public string Timeframe { get; set; }
}
Controller:
[HttpPost]
public void TestControllerMethod(ABCModel model)
{
//this value is null.
var timeFrames = model.Timeframes;
//this value is populated correctly
var timeFrame = model.Timeframe;
}
A form only posts back the name/value pairs of its successful controls. You have created a form control for property Timeframe, so you get the value of the selected option in the POST method.
You have not (and should not), created form controls for each property of each SelectListItem in your Timeframes property, so nothing relating to it is send in the request when the form is submitted, hence the value of Timeframes is null.
If you need to return the view because ModelState is invalid, then you need to re-populate the TimeFrames property as you did in the GET method (otherwise your DropDownListFor() will throw an exception). A typical implementation migh look like
public ActionResult Create()
{
ABCModel model = new ABCModel();
ConfigureViewModel(model);
return View(model);
}
[HttpPost]
public ActionResult Create(ABCModel model)
{
if (!modelState.IsValid)
{
ConfigureViewModel(model);
return View(model);
}
// Save and redirect
}
private void ConfigureViewModel(ABCModel model)
{
model.TimeFrames = ....; // your code to populate the SelectList
}

URL Parsing - routing or other?

I am in the process of migrating PHP code to ASP.NET MVC and previously for the register page I would store if the new user had accepted the rules and also was COPPA verified by redirecting from /register to /register&readrules=1&coppa=1. I would then just parse the #readrules and #coppa in the code.
What is the best way to do this in ASP.NET? Thanks
Use query string parameters instead:
/register?readrules=1&coppa=1
This is more standard and you do not need any parsing. Just define a view model to accomodate those values:
public class MyViewModel
{
public int Readrules { get; set; }
public int Coppa { get; set; }
}
and ten have your Register controller action take this view model as parameter:
public ActionResult Register(MyViewModel model)
{
... at this stage model.Readrules and model.Coppa will contain the values passed
as query string parameters tat you could use here
}
The default model binder will automatically bind the values of the readrules and coppa query string parameters to the corresponding properties of the view model that your controller action takes.

In a post method, how to get compact parameters instead of all the object properties

Maybe the title is not so explicitly. Let me explain you my situation
I've got a get and post method in my controller. In the GET method, gets the entities from the database context
[HttpGet]
public ActionResult RecheckAssignment(short id)
{
var assignment = db.Assignments.Find(id);
Session["QuestionList"] = QuestionRepositoryManager.GetAllPossibleQuestionsFromJson(assignment.Content); // it's a list!
return View(Session["QuestionList"]);
}
Assignment entity contains as 10 properties. When I show this entities in the model, it shows uses all the properties, but when the user does post should get only two properties from it (Id string, Changed bool) in the POST METHOD.
I do not what to put inside of the method parameters.
[HttpPost]
public ActionResult RecheckAssignment(...)
{
return View();
}
I put everything in a session variable because later I must have to get the entities again, I guess this is a good option using Session but I'm not sure.
So, what should I have to write inside of the method to get only the Id and Changed properties to updated the entities.
When ASP.NET MVC maps a <form> back to the Action during a POST it will fill in what it can. Consider a class like this:
public class Car
{
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
}
and now consider this form:
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post))
{
Html.TextBoxFor(m => m.Make)
}
and now consider this Action:
public ActionResult ActionName(Car model)
{
// the values of Car will look like this
model.Make // this will be what was in the text box
model.Model // this will be null
model.Year // this will be 0
}
and take note that null and 0 are the default values for those types. So, if I wanted to POST the property Model I need to get it in the form. I can do that with #Html.TextBoxFor, but what if I don't want the user to see it? Well, I can do that too:
Html.HiddenFor(m => m.Model);
and so now when the form is POSTed it will populate the Model with the value it was downloaded with. So, just make sure that all the properties you need are in the form in some way.

asp.net mvc form values display

I'm new to asp.net mvc. Basically i'm from php programmer. In my php file i can display what are all the values coming from html page or form using echo $_POST; or print_r($_POST); or var_dump($_POST). But in asp.net how can i achieve this to check what are all the values are coming from UI Page to controller.
You may take a look at the Request.Form property:
public ActionResult SomeAction()
{
var values = Request.Form;
...
}
You could put a breakpoint and analyze the values. Or simply use a javascript development toolbar in your browser (such as FireBug or Chrome Developer Toolbar) to see exactly what gets sent to the server.
But normally you are not supposed to directly access the raw values. In ASP.NET MVC there's a model binder which could instantiate some model based on the values sent to the server.
For example you could have the following model:
public class MyViewModel
{
public int Age { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
and then have your controller action take this model as parameter:
public ActionResult SomeAction(MyViewModel model)
{
... you could use the model properties here
}
and now you could invoke this controller action either wityh a GET request passing the parameters in the query string (/someaction?age=10&firstname=foo&lastname=bar) or using a POST and sending them in the body.
You can check the raw data via Request.Form.
But this is not he spirit of the ASP.NET MVC. It is preferd that you expect a model into your controller. You have all type safety mapping already done by special module called model binder.
So unless you work on some special case, you just add a model to the controller action:
public ActionResult SomeAction(SomeModel model)
{
//Handle SomeModel data further ...
}
You can create an action which will accept the parameters from the UI page like the following:
[HttpPost]
public ActionResult SomeAction(string param1, int param2)
{
//Now you can access the values here
}
or make an action which will accept the model
public ActionResult SomeAction(SomeModel model)
{
//Access the model here
}

Resources