Validating TextBox input for a list that is a property of an MVC3 ViewModel - asp.net-mvc-3

I realize I'm a little late to the party, but... I'm working in my first MVC project, and have been able to get a handle on most of what needs to be done. Most of the project simply reads data and pumps the data into charts. However, I have one View whose model looks like this (the parent class properties are not important here):
public class Class1 : ParentClass
{
public List<ChildClass> ChildClassList{get;set;}
}
and the ChildClass looks like this:
public class ChildClass
{
public int Property1{get;set;}
public int Property2{get;set;}
public string Property3{get;set;}
public int? ID{get;set;}
[Editable(true)]
public decimal? Property4{get;set;}
}
Now, retreiving the data is not an issue. I can loop through the list, and create a table for editing like this:
<% foreach(var g in Model.ChildClassList){%>
<tr>
<td style="text-align: right;">
<%= Html.Label(g.Property3)%>
</td>
<td>
<%=Html.TextBox(Model.ParentProperty.ToString() + "_" + g.Property2, (g.Property4.HasValue ? g.Property4.Value.ToString("C") : "$0.00"))%>
</td>
</tr>
<% }%>
After cruising through this site for the past couple of days, it dawned on me that I can validate the input on the server-side, in the POST method (there is a "Save" button at the bottom of the form), but (a)how do I get the validation error message back to the user, (b)perform the validation client-side?.
I must mention also that this view uses the values in the list to create a portion of a chart, prior to being rendered as a table.

On the server-side in the [HttpPost] action, you can check the validity of the model like this:
[HttpPost]
public ActionResult Save(Class1 model)
{
if (!ModelState.IsValid)
return View(model);
// Code to save model.
}
You also need to update your View to show the errors:
<%= Html.ValidationSummary(false, "Please fix these errors.")
<% foreach(var g in Model.ChildClassList){%>
<tr>
<td style="text-align: right;">
<%= Html.Label(g.Property3)%>
</td>
<td>
<%=Html.TextBox(Model.ParentProperty.ToString() + "_" + g.Property2, (g.Property4.HasValue ? g.Property4.Value.ToString("C") : "$0.00"))%>
<%= Html.ValidationMessageFor(model => g.Property4)
</td>
</tr>
<% }%>
If you want to enable it client-side, you need to use unobstrusive client validation, which you can do by updating your web.config:
<configuration>
<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
</configuration>
Also you need the following JS libraries:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" type="text/javascript"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js" type="text/javascript"></script>
<script src="//ajax.microsoft.com/ajax/jQuery.Validate/1.7/jQuery.Validate.min.js" type="text/javascript"></script>
<script src="//ajax.aspnetcdn.com/ajax/mvc/3.0/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>
On a side note, try and avoid loops to render out your View. It's unnecessary code soup, which can be avoided by the use of editor templates.

Related

ModelAttribute returns null values in controller in Spring MVC

Ok, its time to seek help; I am sending a (shopping) Cart ModelAttribute to my jsp, allowing the user to edit the quantity, when the Model is POST to the controller the fields are null except the editable (quantity) field. I have researched for days on similar issues but nothing is matching. I am using spring 3.1.
Here is my controller on the GET and POST:
#Controller
public class CartController {
#Autowired
private Cart cart;
#RequestMapping(value = "/cart", method = RequestMethod.GET)
public String showCart(Model model) {
logger.debug("CartController.showCart() Cart: {}", this.cart);
model.addAttribute(cart);
return "cart/cart";
}
and POST
#RequestMapping(value = "/cart", method = RequestMethod.POST, params = "update")
public String update(#ModelAttribute("cart") Cart cart, BindingResult result, Model model) {
logger.debug("CartController.update() Cart: {}", cart);
return "cart/cart";
}
my jsp:
<div class="container MainContent">
<form:form method="POST" modelAttribute="cart">
<fieldset>
<legend>Cart</legend>
<table class="table">
<thead>
<tr>
<th>Product Name</th>
<th>Quantity</th>
<th>Product Price</th>
</tr>
</thead>
<tbody>
<c:forEach items="${cart.cartDetails}" var="cartDetail" varStatus="status">
<tr>
<td>${cartDetail.product.name}</td>
<td><form:input path="cartDetails[${status.index}].quantity" size="1" /></td>
<td>${cartDetail.price}</td>
</c:forEach>
<tr>
<b><td colspan="2" align="right"><spring:message code="order.total" /></b>
</td>
<td>${cart.totalCartPrice}</td>
</tr>
</tbody>
</table>
</fieldset>
<div></div>
<button id="order" name="order">
<spring:message code="button.order" />
</button>
<button id="update" name="update">
<spring:message code="button.update" />
</button>
</form:form>
</div>
and the log results for cart before on GET:
CartController.showCart() Cart: Cart [cartDetails=[CartDetail
product=com.Product#c26440[name=My Name],
quantity=1]], totalCartPrice=10.00]
and after updating the quantity from 1 to 3 in the jsp and then POST to the controller:
CartController.update() Cart: Cart [cartDetails=[CartDetail
[product=null, quantity=3]], totalCartPrice=null]
I've read several similar post here and on the Spring forum and tried different suggested solutions with no luck. It seems like my edited quantity results are getting bound to the Object correctly but why aren’t the others?
Assuming you have all the necessary fields in your Form object;
You have to specify the form fields and fill the value with your data.
<td>${cartDetail.product.name}</td>
will only print the result to the screen. If you want to bind it to your form you have to put it in a spring form input such as:
<form:input path="productName" value="${cartDetail.product.name}"/>
If you don't want it to be editable then you can put it into a hidden field but in the end you'll have to put it in a form element in the jsp and have a corresponding field in your form POJO
Seems other fields aren't bound, try to bind for example product name
<td>${cartDetail.product.name}
<form:hidden path="cartDetails[${status.index}].product.name" value="${cartDetail.product.name}"/></td>
I once spent a lot of time investigating a similar issue. Finally I found the culprit inside a Binder's initialization method:
#InitBinder
void initBinder(final WebDataBinder binder) {
binder.setAllowedFields("name", ...);
}
This method sets a restriction on fields that are allowed for binding. And all the other fields are unbound, naturally resulting in null values.
The other possible reason: incorrect setters in a Bean annotated with #ModelAttribute. For example, Object setName(String name) instead of void setName(String).

Ajax.BeginForm with UpdateTarget Inside AjaxForm

Is this possible to use Ajax.Beginform with update target inside of ajax form. like this:
using(Ajax.BeginForm("EditPhone", new { id = item.Id.Value }, new AjaxOptions {
UpdateTargetId = "TRTarget"})) {
<tr class="gradeA odd" id="TRTarget">
<input type"submit" value="submit" />
</tr>
}
Update
OK if it's possible so what is wrong with this?
This is my partial view that another partial view rendered inside it:
using(Ajax.BeginForm("EditPhone", new { id = item.Id.Value }, new AjaxOptions {
UpdateTargetId = "TRTarget"})) {
<tr class="gradeA odd" id="TRTarget">
#{Html.RenderPartial("_PhoneRow", item);}
</tr>
}
and _PhoneRow:
#model MyModel
<td>#Html.DisplayFor(model=>model.Number)</td>
<td>#Html.DisplayFor(modelItem => Model.PhoneKind)</td>
<td><input type="submit" value="Edit" class="button" /></td>
And EditPhone Action:
public ActionResult EditPhone(long Id){
//Get model
return PartialView("_EditPhoneRow", model);
}
And _EditPhoneRow:
<td>#Html.EditorFor(model => model.MainModel.Number)</td>
<td>#Html.EditorFor(model => model.MainModel.PhoneKind)</td>
<td><input type="submit" value="Save" class="button" /></td>
Actually each of my rows have an Ajax form so when click on edit I want to replace the row with another as you see, but when I add the Edit, all of my page destroyed and just _EditPhoneRow shown like I select all page for updateTrget where is the problem? and what is your suggestion to change all the specific row like this?
According to the HTML specification forms cannot be nested. This produces invalid HTML and depending on the user agent either the outer or the inner <form> simply won't work. That's a limitation of the HTML specification, don't be confused with ASP.NET MVC, it has nothing to do with it. One possibility is to replace your Ajax.BeginForm with an Ajax.ActionLink:
<tr class="gradeA odd" id="TRTarget">
#Ajax.ActionLink(
"Submit",
"EditPhone",
new { id = item.Id.Value },
new AjaxOptions { UpdateTargetId = "TRTarget" }
)
</tr>
UPDATE:
After you have updated your question and explained the symptoms I think you might have forgotten to reference the jquery.unobtrusive-ajax.min.js script to your page:
<script type="text/javascript" src="#Url.Content("~/scripts/jquery.unobtrusive-ajax.min.js")"></script>
If you don't include this script the Ajax.* helpers such as Ajax.BeginForm and Ajax.ActionLink will be simple HTML forms and anchors. No AJAX at all. It is this script that reads the HTML5 data-* attributes emitted by those helpers and unobtrusively AJAXifies them.

help me ! MVC and AJAX toolkit Editor in ASP.NET

I have view to use Ajax toolkit editor control.
View CreateProduct
<fieldset>
<legend>Product information</legend>
<table align="center">
<tr>
<td><label for="slogan">Slogan:</label></td>
<td><%= Html.TextBox("slogan")%></td>
</tr>
<tr>
<td><label for="content">Content :</label></td>
<td>
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<cc1:Editor ID="content" runat="server" Height="300px" />
</td>
</tr>
</table>
</fieldset>
ProductController:
public ActionResult CreateProduct(string slogan, string content)
{
ProductDataContext data = new ProductDataContext();
PRODUCT p = new PRODUCT();
p.SLOGAN = slogan;
p.CONTENT = content;
data.AddProduct(p);
data.SubmitChanges();
return View();
}
When I added a product, just slogan was added, content was null.
I dont understand and how to repair it.
Help me, please!
Thanks so much!
It does not work this way. You are mixing ASP.NET WebForms with MVC. ID="content" only sets the server-side ID of the Editor control. Controller parameters however are mapped by form field names and in your case the name of the corresponding textarea is generated automatically. I'm not aware of any way you could normally change the name of a control rendered by ASP.NET. You can, however try the following:
<script type="text/javascript">
document.getElementById('<%= content.ClientID =>').name = 'content';
</script>
Put this at the bottom of your view. It might just work.
Keep in mind that even if it works, the above is a dirty hack. The right approach in an MVC project would be to initialize the Editor control using just client scripting. This is not always easy but doable. For reference, try looking at the source of this page:
http://www.asp.net/ajax/ajaxcontroltoolkit/samples/htmleditor/OtherSamples/ClientSide.htm

ASP.NET MVC3 Model Binding using IEnumerable (Cannot infer type from)

I have a model class (edited for brevity)
Model Class
public class GridModel
{
public string ItemNumber { get; set; }
public int OnHandQty { get; set; }
}
public class Shipment
{
public string shipTrackingNo {get; set;}
public IEnumerable<GridModel> ItemsShipped { get; set;}
{
cshtml page
#model Namespc.Models.Shipment
<link href="../../Content/CSS/Grid/Grid.css" rel="stylesheet" type="text/css" />
<script src="../../Scripts/ECommerce.Grid.js" type="text/javascript"></script>
<div id="_shipmentDetailGrid">
<table class="TableStyle">
<tr class="GridRowStyle">
<td width="130px" ></td>
#foreach (var Item in Model.ItemsShipped)
{
<td width="70px" align="center">
#html.LabelFor(item.OnHandQty) <-- Cannot infer type from usage
</td>
}
</tr>
I want to be able to bind item.OnHandQty that resides in the IEnumerable collection. How can you have a Model class and also an IEnumerable collection of a custom class (or rather you own class)?
Well, what is the type of the items stored in ItemsShipped? You should use the generic version of IEnumerable to indicate what types are stored within it.
If your class was named Item, you would declare it IEnumerable<Item> then, when iterating at run time, ie #foreach (var Item in Model.ItemsShipped), the type of Item will be strongly-typed instead of a plain object.
Instead of this:
#foreach (var Item in Model.ItemsShipped)
{
<td width="70px" align="center">
#html.LabelFor(item.OnHandQty) <-- Cannot infer type from usage
</td>
}
Do this:
#Html.DisplayFor(model => model.ItemsShipped)
Then create a custom display template (placed in Views/Shared/DisplayTemplates/GridModel.cshtml):
#model Namespc.Models.GridModel
<td width="70px" align="center">
#html.LabelFor(model => model.OnHandQty)
</td>
I've got a feeling it's not working because you're not passing an expression to the LabelFor method.
The above is much nicer and robust than an explicit for loop.

requesting parameters from jsp

I have some problems with taking a parameters from jsp page, when method POST occurs.
My JSP page looks like this:
....
<table border="1">
<tr>
<th>name</th>
<th>check</th>
</tr>
<c:forEach items="${things}" var="pair">
<tr>
<td>${things.name}</td>
<td><INPUT TYPE="CHECKBOX" NAME=items VALUE=${things.id} ></td>
</tr>
</c:forEach>
</table>
<form method="post">
<input type="submit" value="Check all" />
</form>
So, I want to take all checked "things" in table. In controller class I something like this (written in Spring):
....
#RequestMapping(method = RequestMethod.POST)
public String sumbitForm(#RequestParam("items") String[] items){
if(items!= null){
for(String item: items){
....
}
}
return "redirect:myPage";
}
But my app don't want to work with such RequesParam. It doesn't put the values of items parameter to it. (this method I took here http://www.go4expert.com/forums/showthread.php?t=4542)
Also I tried using #ModelAttribute instead of #RequesParam. When I'm using it, my app don't give a errors, but it also couldn't correctly put the "items" to this parameter.
Any ideas?
P.S. May be you know more better method of taking list of parameters from JSP page for using their values (like taking checked items)?
Your table is outside of the <form></form> so when submitting, it doesnt send anything.

Resources