ASP.NET MVC2 model binding problem - asp.net

Why is my controller receiving an empty model in this case?
Using
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<X.Models.ProductModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<h2>Product</h2>
<% using (Html.BeginForm() {%>
<%: Html.ValidationSummary(true) %>
<div class="editor-label">
Product Name
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Name) %>
<%: Html.ValidationMessageFor(model => model.Name) %>
</div>
<br />
<div class="editor-label">
Short Description
</div>
<div class="editor-field">
<%: Html.TextAreaFor(model => model.ShortDesc) %>
<%: Html.ValidationMessageFor(model => model.ShortDesc) %>
</div>
<br />
<div class="editor-label">
Long Description
</div>
<div class="editor-field">
<%: Html.TextAreaFor(model => model.LongDesc) %>
<%: Html.ValidationMessageFor(model => model.LongDesc) %>
</div>
<p>
<input type="submit" value="Create" />
</p>
<% } %>
</asp:Content>
and the following controller.
using System.Web.Mvc;
using X.Lib.Services;
using X.Models;
namespace X.Admin.Controllers
{
public class ProductController : Controller
{
[HttpGet]
public ActionResult ProductData()
{
return View();
}
[HttpPost]
public ActionResult ProductData(ProductModel NewProduct)
{
//Validate and save
if(ModelState.IsValid)
{
//Save And do stuff.
var ProductServ = new ProductService();
ProductServ.AddProduct(NewProduct);
}
return View();
}
}
}
Model:
public class ProductModel
{
public int ID;
[Required(ErrorMessage = "Name is required")]
public string Name;
public string LongDesc;
public string ShortDesc;
}

EDIT: you need to use properties not variables
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
public string LongDesc { get; set; }
public string ShortDesc { get; set; }
Also...
You are not passing the model back to the view.
return View(NewProduct);
I normally pass a blank model in to the GET action too
ProductModel NewProduct = new ProductModel();
return View(NewProduct);
This way if you wish to set any default values you can do so easily.
Code example in full I've also added try and catch blocks around the adding of the product and given example views you could be returning on success or fail:
[HttpGet]
public ActionResult ProductData()
{
ProductModel NewProduct = new ProductModel();
return View(NewProduct);
}
[HttpPost]
public ActionResult ProductData(ProductModel NewProduct)
{
//Validate and save
if(!ModelState.IsValid)
{
// Return the model back to view
return View(NewProduct);
}
try{
//Save And do stuff.
var ProductServ = new ProductService();
ProductServ.AddProduct(NewProduct);
}
catch(Exception){
return View("Fail");
}
return View("Success");
}

Related

Server Side DropDown validation firing always

My MVC 2 dropdown validation fire always even though i select a item from the list. I checked from firebug the dropdown is bind correcly with expected values.
Model:
public class Delivery
{
public int DeliveryID { get; set; }
[Required(ErrorMessage = "Please Select a Stock")]
public int? StockID { get; set; }
[Required(ErrorMessage = "Please Enter Expenses")]
public double OtherExpenses { get; set; }
public double Total { get; set; }
[Required(ErrorMessage = "Please Enter Description")]
public string Description { get; set; }
[Required(ErrorMessage = "Please Enter Arrived Date")]
public DateTime ArrivedDate { get; set; }
public string StockDescription { get; set; }
public IEnumerable<Stock> lstStock { get; set; }
}
Controller:
public ActionResult Create()
{
DeliveryRepository rep = new DeliveryRepository();
var model = new Delivery
{
lstStock = rep.GetStock()
};
return View(model);
}
//
// POST: /Delivery/Create
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
Delivery del = new Delivery();
DeliveryRepository rep = new DeliveryRepository();
TryUpdateModel(del);
if (ModelState.IsValid)
{// TODO: Add insert logic here
del.ArrivedDate = Convert.ToDateTime(Request.Form["ArrivedDate"]);
del.Description = Request.Form["Description"];
del.OtherExpenses = Convert.ToDouble(Request.Form["OtherExpenses"]);
del.StockID = Convert.ToInt32(Request.Form["StockID"]);
del.Total = Convert.ToDouble(Request.Form["lblTotal"]) + del.OtherExpenses;
rep.Create(del);
rep.Save();
return RedirectToAction("Index");
}
else
{
var model = new Delivery
{
lstStock = rep.GetStock()
};
return View(model);
}
}
catch
{
return View();
}
}
View:
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.Label("Select Stock") %>
</div>
<div class="editor-field">
<%: Html.DropDownListFor(x => x.lstStock, new SelectList(Model.lstStock, "StockID", "Description"), "-- Please Select a Stock --")%>
<%: Html.ValidationMessageFor(model => model.StockID)%>
</div>
<div id="clslbl">
<br />
<label id="lblTotal"></label>
</div>
<br />
<div class="editor-label">
<%: Html.LabelFor(model => model.OtherExpenses) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.OtherExpenses) %>
<%: Html.ValidationMessageFor(model => model.OtherExpenses) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Description) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Description) %>
<%: Html.ValidationMessageFor(model => model.Description) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.ArrivedDate) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.ArrivedDate) %>
<%: Html.ValidationMessageFor(model => model.ArrivedDate) %>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
Issue was with my view
<div class="editor-field">
<%: Html.DropDownListFor(x => x.lstStock, new SelectList(Model.lstStock, "StockID", "Description"), "-- Please Select a Stock --")%>
<%: Html.ValidationMessageFor(model => model.StockID)%>
</div>
Should be changed as
<div class="editor-field">
<%: Html.DropDownListFor(x => x.StockID, new SelectList(Model.lstStock, "StockID", "Description"), "-- Please Select a Stock --")%>
<%: Html.ValidationMessageFor(model => model.StockID)%>
</div>

MVC 2 model with IList<T> properties binding question

I'm working on an MVC 2 project and I have a model that looks like this:
public string AccountNumber { get; set; }
public IList<Equipment> ShippedEquipmentList { get; set; }
and a view that has a button for adding a new piece of equipment. Clicking the button dynamically adds new textboxes to the view for specifying another piece of equipment. The partial view it renders looks like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<RmaMVC.Models.Entities.Equipment>" %>
<div class="editorRow">
Item: <%: Html.TextBoxFor(x => x.ItemID); %>
Value: <% Html.TextBoxFor(x => x.Description); %>
</div>
My question is: how do I bind this data to the model? When the controller gets called the ShippedEquipmentList comes back as null.
Edit: here is what I have so far. My model looks like this:
public string AccountNumber { get; set; }
public IList<Equipment> ShippedEquipmentList { get; set; }
FormInputs()
{
ShippedEquipmentList = new List<Equipment>();
// adding a single blank piece of equipment so that the length isn't 0
Equipment blank = new Equipment();
ShippedEquipmentList.Add(blank);
}
my main view is this:
<% Html.BeginForm(); %>
<div id="items">
</div>
<%: Ajax.ActionLink("add new", "AddNewEquipment", new AjaxOptions {
UpdateTargetId = "items", InsertionMode = InsertionMode.InsertAfter }) %>
<input type="submit" value="submit" />
<% Html.EndForm(); %>
my controller:
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(FormInputs input)
{
return View(input);
}
public ActionResult AddNewEquipment()
{
return PartialView("~/Views/RMA/EditorTemplates/Equipment.ascx");
}
the partial view that generates the text boxes for the equipment:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<RmaMVC.Models.Entities.Equipment>" %>
<p>
<%: Html.TextBoxFor(x => x.ItemID) %>
<%: Html.TextBoxFor(x => x.Description) %>
<%: Html.TextBoxFor(x => x.Quantity) %>
<%: Html.TextBoxFor(x => x.SerialNumber) %>
</p>
Check this: Model Binding To A List

MVC2: How submit data from Partial view to his controller?

I have this model:
public class Package
{
public string CustomerName { get; set; }
public List<Product> Products { get; set; }
public int Id { get; set; }
}
public class Product
{
public int Quantity { get; set; }
public string Name { get; set; }
public int Id { get; set; }
}
I have a Create's view for creating a new Package and one or many Create product's PartialView.
How I can submit the Product informations to the Product's controller without leave the View?
Here is my Product's Partialview code:
<% using (Ajax.BeginForm("Create", "Product", new AjaxOptions())) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Name) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Name) %>
<%: Html.ValidationMessageFor(model => model.Name) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Quantity) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Quantity) %>
<%: Html.ValidationMessageFor(model => model.Quantity) %>
</div>
<%-- <p>
<input type="submit" value="Create" />
</p>--%>
</fieldset>
<% } %>
Thank you
You can use AJAX to solve your problem. The code pode below post 3 values to YourController in YourAction.
jQuery('#YourButton').click(function (event) {
var jqxhr = $.post("YourController/YourAction", {
lastName: $("#tbLastName").val(),
firstName: $("#tbFirstName").val(),
id: $("#id").val()
},
function (data) {
$('#WhereResultAppear').html(data); //can be deleted
})
.success(function () {
$('#InCaseOfSuccess').html(data); //can be deleted
})
.error(function (jqXHR, status, error) {
$('#InCaseOfError').html(data); //can be deleted
})
.complete(function () {
$('#WhenActionIsComplete').html(data); //can be deleted
});
});
Give an id to your form in the partial view. and then use JQuery to send data by serializing the form.
$.post("controller/action", $("#formId").serialize(),
function(data) {
alert("Data Loaded: " + data);
});
And in the controller
Package pakage = new Package();
UpdateModel(pakage );
Although you can write code to serialize the data as the other answers mention, I would consider surrounding the fields in an
#using (Ajax.BeginForm("ActionOnController", new AjaxOptions{}))
{
}
This will do it all for you (as long as you include the necessary javascript e.g. jquery.unobtrusive-ajax.min.js)
Take a look at the AjaxOptions documentation. This allows you to (amongst other things) update another section after posting the values back. You can use this to indicate success
Martin

MVC2: Is rendering List<object> possible in Create context?

I have this Model:
public class Package
{
public string CustomerName { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
public int Quantity { get; set; }
public string Name { get; set; }
}
When I add the Create's view, the code is:
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.CustomerName) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.CustomerName) %>
<%: Html.ValidationMessageFor(model => model.CustomerName) %>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
How manage the Products list?
Can I get a button or something to create a new product and add it to the Products list?
Thank you
for creating buttn you can also do this use HTML.ActionLink() or Ajax.ActionLink() defined as a button like:
<% Response.Write(Html.ActionLink("Add Product", "Create", new { id = tId, tNum = tNum }, new { #class = "oldVal" })); %>
it'll create a button on your page, and here you can see different attributes (so just check them out you'll find it useful) ... and from both of this which to use is more depends upon your desired action ...
and in your controller:
public ActionResult Create()
{
// do same as mentioned by [gnome][1]
}
here is the example: modal popup, declared as a partial view of page
[partial view using Ajax.ActionLink()]
using (Ajax.BeginForm("Login", "Users", null, new AjaxOptions() { UpdateTargetId = "divLoginPopupContent" }))
{
Response.Write(Html.ValidationSummary(true));
%>
<ul class="chooseQuestion">
<li>
<div class="short">
<%= Html.LabelFor(model => model.LoginEmail)%>
</div>
<div class="editor-field">
<%= Html.TextBoxFor(model => model.LoginEmail)%>
<%= Html.ValidationMessageFor(model => model.LoginEmail)%>
</div>
</li><li>
<div class="short">
<%= Html.LabelFor(model => model.LoginPassword)%>
</div>
<div class="editor-field">
<%= Html.PasswordFor(model => model.LoginPassword)%>
<%= Html.ValidationMessageFor(model => model.LoginPassword)%>
</div>
</li><li>
<div class="checkbox">
<%= Html.CheckBoxFor(model => model.Remember)%>
<%= Html.LabelFor(model => model.Remember)%>
</div>
</li><li>
<input type="submit" class="button" value="Login" id="btnLoginSubmit" />
<div id="divlogin_ajaxloading" style="display:none; vertical-align:top; text-align:center;"><img alt="" src="/images/ajax-loader.gif" /></div>
</li>
</ul>
}
your partial view action:
public ActionResult Login(LoginModel model)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.LoginEmail, model.LoginPassword))
{
return Redirect("/MyPage");
}
else
{
ModelState.Clear();
return PartialView("LoginPopup", new LoginModel());
}
}
}
Just typed this up, haven't tested it but the basic idea is to list your products on the customer view with a button to add a new product for a customer.
To list Products for Customer:
<table>
<% foreach(var p in Customer.Products) { %>
<tr>
<td><%: p.Quantity %></td>
<td><%: p.Name %></td>
</tr>
<% } %>
</table>
<p><%: Html.ActionLink("Add Product", "Create", new { controller = 'Products' }, new { id = 'addProduct'}) %>
<div id="dialog"></div>
To add a new Product for a customer you could use jQuery UI to show a dialog; just pass the Customer Id
// assuming you've included jquery-ui
<script type="text/javascript">
$(document).ready(function() {
$('#dialog').dialog({
autoOpen : false,
button : {
'Save' : function() {
$.ajax({
url : $('#addProduct').attr('href'),
type : 'get'
success : function() {
alert('Product added!');
}
});
},
'Cancel' : function() {
$('#dialog').dialog('close');
}
}
});
$('#addProduct').click(function() {
var customerId = $('#CustomerId').val();
$('#dialog').dialog('open');
$('#Customer_Product_Id').val(customerId); // assuming there's a hidden field on the form
});
});
</script>
In your product controller return a partial
public ActionResult Create()
{
List<Product> products = new List<Product>() {
new Product() { Id = 1, Name = "Rice" },
new Product() { Id = 2, Name = 'Corn' }};
ViewData.Add("Products", new SelectList(products, "Id", "Name", ""));
Product product = new Product();
if (Request.IsAjaxRequest())
{
return PartailView("_CreateOrEdit", product)
}
return View("Create", product);
}
_Create view
<%: Html.TextBoxFor(model => model.Quantity) %>
<%: Html.DropDownList("Products") %>
<%: Html.HiddenFieldFor(model => model.Product.Customer.Id) %>

ModelState always valid

I've got something seemingly very simple not working.
I have got a model
public class Name: Entity
{
[StringLength(10), Required]
public virtual string Title { get; set; }
}
public class Customer: Entity
{
public virtual Name Name { get; set; }
}
a view model
public class CustomerViweModel
{
public Customer Customer { get; set; }
}
a view
<% using(Html.BeginForm()) { %>
<%= Html.LabelFor(m => m.Customer.Name.Title)%>
<%= Html.TextBoxFor(m => m.Customer.Name.Title)%>
<button type="submit">Submit</button>
<% } %>
and a controller
[HttpPost]
public ActionResult Index([Bind(Prefix = "Customer")] Customer customer)
{
if(ModelState.IsValid)
Save
else
return View();
}
No matter what I enter as the title (null, or a string > 10 chars), ModelState.IsValid is always true. The Title field in the Customer object has a value, so the data is being passed around, but not being validated?
Any clues?
In your View I don't see any text box or a field allowing to send data to the controller, only a label. Properties will not be validated if they are not posted. Add a textbox, leave it blank and your model won't be valid any more:
<%= Html.TextBoxFor(m => m.Customer.Name.Title)%>
UPDATE:
Here's the code I've used:
Model:
public class Name
{
[StringLength(10), Required]
public virtual string Title { get; set; }
}
public class Customer
{
public virtual Name Name { get; set; }
}
public class CustomerViewModel
{
public Customer Customer { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index([Bind(Prefix = "Customer")]Customer cs)
{
return View(new CustomerViewModel
{
Customer = cs
});
}
}
View:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyApp.Models.CustomerViewModel>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using(Html.BeginForm()) { %>
<%= Html.LabelFor(m => m.Customer.Name.Title)%>
<%= Html.TextBoxFor(m => m.Customer.Name.Title)%>
<button type="submit">Submit</button>
<% } %>
</asp:Content>
When you submit this form a validation error is shown.
Remark1: I've omitted the Entity base class in the models as I don't how does it look.
Remark2: I've renamed the variable in the Index action to cs. I remember that there was some problems with this in ASP.NET MVC 1.0 when you had the prefix and the variable named the same but I am not sure whether this applies here and I think it was fixed.
Figured it out, it was becuase I'm referencing System.ComponentModel.DataAnnotations 3.6 instead of 3.5. From what I gather, 3.6 is for WCF RIA services only.

Resources