MVC4 model binding with two complex models - asp.net-mvc

I have an action on my controller that takes two parameters that should be captured when a form is posted:
[HttpPost]
public ActionResult Index(MyModel model, FormAction action)
The idea is that the model data should be captured in MyModel and the button that the user pressed should be captured in FormAction:
public class MyModel
{
public string MyValue { get; set; }
}
public class FormAction
{
public string Command { get; set; }
}
Here is my view:
#model TestApp.Models.MyModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
#using (Html.BeginForm("Index", "Home"))
{
#Html.TextBoxFor(x => x.MyValue)
<input type="submit" value="OK" name="command" />
<input type="submit" value="Cancel" name="command" />
}
</div>
</body>
</html>
If I add another string parameter to the action called 'command' then the value of the button comes through but it doesn't get bound to the Command property on the FormAction parameter - the parameter is always null.
If I add a Command property to MyModel then the button value does come through.
Is there something in MVC model binding that prevents more than one complex model to be bound in one action method?

The metter is that Html.BeginForm sends only model from top statenent: #model TestApp.Models.MyModel. If I clearly understand what you whant to do, the better solution will be to create ViewModel:
public class ViewModel
{
public MyModel myModel {get; set;}
public FormAction formAction {get; set;}
}
Change View as follows:
#model TestApp.Models.ViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
#using (Html.BeginForm("Index", "Home"))
{
#Html.TextBoxFor(model=> model.myModel.MyValue)
#Html.TextBoxFor(model=> model.formAction.Command)
<input type="submit" value="OK" name="command" />
<input type="submit" value="Cancel" name="command" />
}
</div>
</body>
</html>
And change your Action to:
[HttpPost]
public ActionResult Index(ViewModel model)

I've got to the bottom of this and the reason it wasn't working was simply because the parameter was named action. This is pretty much the equivalent of a keyword in MVC and it is used by the MVC framework to identify the action to be executed.
Changing the parameter name to something else means that the Command property comes through as expected!

Related

Server Error in '/' Application in MVC 5 application

I am currently learning MVC 5 by watching video tutorials. I have created one simple customercontroller with two action methods i.e. (AddCustomer and Submit). Here i have created one simple view for AddCustomer and strongly typed view for Showing customer data. When i start application it shows me the customer data entry screen but when i click on submit button i am getting below error.
Can you please tell me what is the issue in my below code?
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /Submit
Here's my Customer Controller Code:-
public class CustomerController : Controller
{
// GET: Customer
public ActionResult AddCustomer()
{
return View();
}
public ActionResult Submit(Customer objcust)
{
objcust.CustomerID = Request.Form["txtcustid"];
objcust.CustomerName = Request.Form["txtcustname"];
return View("ShowCustData", objcust);
}
}
Here's My ShowCustData View:-
#model DataAnnotationsEx.Models.Customer
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>ShowCustData</title>
</head>
<body>
<div>
Customer ID: #Model.CustomerID <br />
Customer Name: #Model.CustomerName
</div>
</body>
</html>
Here's My AddCustomer View:-
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>AddCustomer</title>
</head>
<body>
<div>
<form action="Submit" method="post">
Customer ID: <input id="Text1" type="text" name="txtcustid" /><br />
Customer Name: <input id="Text1" type="text" name="txtcustname" /><br />
<input id="Submit1" type="submit" value="submit" />
</form>
</div>
</body>
</html>
Here's My Customer Model:-
public class Customer
{
[Required]
[StringLength(7)]
[RegularExpression("^[A-Z]{3,3}[0-9]{4,4}$")]
public string CustomerID { get; set; }
[Required]
[StringLength(10)]
public string CustomerName { get; set; }
}
Here's My Route.config:-
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Customer", action = "AddCustomer", id = UrlParameter.Optional }
);
}
The problem is your Submit controller action doesn't have [HttpPost] attribute, where your form method defined as POST. You should add the mentioned attribute on corresponding action method:
[HttpPost]
public ActionResult Submit(Customer objcust)
{
objcust.CustomerID = objcust.CustomerID;
objcust.CustomerName = objcust.CustomerName;
return View("ShowCustData", objcust);
}
Note that by default if [HttpPost] attribute not specified, it automatically sets as GET method, hence the action method never be reached by form submit request.
Then, you should replace <form> tag with Html.BeginForm() helper with strongly-typed viewmodel definition like this:
#model Customer
#using (Html.BeginForm("Submit", "Customer", FormMethod.Post))
{
#Html.LabelFor(model => model.CustomerID)
#Html.TextBoxFor(model => model.CustomerID)
#Html.LabelFor(model => model.CustomerName)
#Html.TextBoxFor(model => model.CustomerName)
<input id="Submit1" type="submit" value="submit" />
}
By using strongly-typed viewmodel class, the textboxes are bound automatically to viewmodel properties and be able to retrieve values during form submission.

ASP.NET Core MVC model binding to list returns empty list in POST method? [duplicate]

I have a really wierd issue with MVC.
My models keeps getting submitted empty.
And it's probably really simple, but I just can't find the issue.
My model looks like this:
public class LoginModel
{
public string Username;
public string Password;
}
My controller like this:
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LoginModel loginTest)
{
if (loginTest.Username != "x" && loginTest.Password != "y")
{
ModelState.AddModelError("a", "Login failed.");
return View(loginTest);
}
else
{
return RedirectToAction("Home", "Welcome");
}
}
And the view is also very simple, like this.
#model LoginSolution.Models.LoginModel
#{
Layout = null;
}
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Login</title>
</head>
<body>
#using (Html.BeginForm("Login", "Home"))
{
<div> <span>Username : </span>
#Html.EditorFor(model => model.Username)
<br />
<span>Password : </span>
#Html.EditorFor(model => model.Password)
<br />
#Html.ValidationSummary()
<br />
<input type="submit" value="Login" name="Login" />
</div>
}
</body>
</html>
This is not a question about security or best practice.
This is just a question regarding why the model keeps returning itself as empty upon submitting.
Your model contains fields, not properties (no getter/setter) so the model binder cannot set the values. Change your model to
public class LoginModel
{
public string Username { get; set; }
public string Password { get; set; }
}

can we use html tags in asp.net mvc

I want to use following html tags in asp.net mvc,is it possible?
<form action="demo_form.asp">
First name: <input type="text" name="fname"><br>
Last name: <input type="text" name="lname"><br>
<input type="submit" value="Submit">
</form>
You just have to make your view as strong typed view using these code u get able to use this code.I hope this will help u.
C# Controller Code
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(sample b)
{
return View();
}
C# Class Code
public class sample
{
public string fname { get; set; }
public string lname { get; set; }
}
Razor View Code
#model WebApplication1.Models.sample
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
#using (Html.BeginForm("index","Home"))
{
<input type="text" name="fname"><br>
<input type="text" name="lname"><br>
<input type="submit" value="Submit">
}
</body>
</html>
If you do not want to create a Strongly Typed View then you can just do like this.
[HttpPost]
public ActionResult Index(string fname, string lname)
{
return View();
}

Why is Model Binding not working in my POST action method?

I have a really wierd issue with MVC.
My models keeps getting submitted empty.
And it's probably really simple, but I just can't find the issue.
My model looks like this:
public class LoginModel
{
public string Username;
public string Password;
}
My controller like this:
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LoginModel loginTest)
{
if (loginTest.Username != "x" && loginTest.Password != "y")
{
ModelState.AddModelError("a", "Login failed.");
return View(loginTest);
}
else
{
return RedirectToAction("Home", "Welcome");
}
}
And the view is also very simple, like this.
#model LoginSolution.Models.LoginModel
#{
Layout = null;
}
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Login</title>
</head>
<body>
#using (Html.BeginForm("Login", "Home"))
{
<div> <span>Username : </span>
#Html.EditorFor(model => model.Username)
<br />
<span>Password : </span>
#Html.EditorFor(model => model.Password)
<br />
#Html.ValidationSummary()
<br />
<input type="submit" value="Login" name="Login" />
</div>
}
</body>
</html>
This is not a question about security or best practice.
This is just a question regarding why the model keeps returning itself as empty upon submitting.
Your model contains fields, not properties (no getter/setter) so the model binder cannot set the values. Change your model to
public class LoginModel
{
public string Username { get; set; }
public string Password { get; set; }
}

jquery unobtrusive doesn't emit validation attributes client-side when having a child grid

I have setup a little test project that I hope will explain my problem.
I have a parent class that has a property containing all its children of type Child. In the MVC view I list all the children in a table and to have the form post back my children and map them to the parent property automatically I render them like:
#Html.TextBox(string.Format("Children[{0}].ChildName", childIndex), child.ChildName)
In the controller I have marked property ChildName to be required. The problem I have is that jquery unobtrusive validation does not emit any data validation attributes. Everything validates nice on the server but not on the client (obviously because there are no attributes for jquery validation to find on these inputs).
Please have a look at the code:
VIEW
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
<form action="#Url.Action("Save")" method="post">
#Html.TextBox("Name", (string)Model.Name)
<table>
<tr><td>Child name</td></tr>
#{var childIndex = 0;}
#foreach (var child in (List<GridValidationTest.Controllers.Child>)Model.Children)
{
<tr><td>#Html.TextBox(string.Format("Children[{0}].ChildName", childIndex), child.ChildName)</td></tr>
childIndex++;
}
</table>
<br /><button>Submit</button>
</form>
</div>
</body>
</html>
CONTROLLER
namespace GridValidationTest.Controllers
{
public class Parent
{
[Required]
public string Name { get; set; }
public IList<Child> Children { get; set; }
}
public class Child
{
[Required]
public string ChildName { get; set; }
}
public class MyController : Controller
{
//
// GET: /My/
public ActionResult Index()
{
var parent = new Parent { Name = "Parent name" };
parent.Children = new List<Child>
{
new Child {ChildName = "First child"},
new Child {ChildName = "Second child"}
};
return View("Index", parent);
}
public ActionResult Save(Parent parent)
{
return View("Index", parent);
}
}
}
The client validation on property Name of Parent class works fine as expected.
How should I render the children to have client-side validation to work as I expect?
You need to bind the fields in your view to your model. Instead of using:
#Html.TextBox(string.Format("Children[{0}].ChildName", childIndex), child.ChildName)
Use
#Html.TextBoxFor(model => model.Children[childIndex])
#Html.ValidationMessageFor(model => model.Children[childIndex])
Also make sure you are including the right jquery javascript files on your page. Something like:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
Also make sure that in your web config you have ClientValidationEnabled and UnobtrusiveJavaScriptEnabled turned on like so:
<appSettings>
....
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
....
</appSettings>

Resources