Dwight Mendoza Dwight Mendoza - 9 months ago 31
jQuery Question

Dropdownlist selection saving id instead of name

I am new to MVC and jQuery. The following issue involves two tables, Product and QuoteDetail. I am using a DropDownListFor to allow the user to select products from the Product table based upon Name. Once the Name, is selected, two textboxes in the QuoteDetail record are populated with the ProductId, and the Price, respectively. All that is working great. My problem is that when I save the QuoteDetail record, it saves the ProductId in the ProductName field instead of the ProductName. I don't see how I can get away from using the ProductId in the SelectList, as I need it to perform the lookup. How can I get around this and save the ProductName?

Here is the way the DropDownListFor reads:

@Html.DropDownListFor(model => model.ProductName, new SelectList(ViewBag.ProductData, "ProductId", "Name"), new { htmlAttributes = new { @id = "ProductName", @class = "ProductList" } });


The jQuery for the lookup is as follows:

<script type="text/javascript">
$(document).ready(function () {
$(document).on("change", '[id*="ProductName"]', function () {
$.post("/QuoteViewModel/GetProduct", { pId: $(this).val() }, function (data) {
$("[id*='ProductId']").val(data.ProductId);
$("[id*='Price']").val(data.Price);
});
});
});
</script>


and the controller action for the lookup is as follows:

[HttpPost]
public ActionResult GetProduct(int pId)
{
var data = db.Products.Find(pId);
return Json(data);
}


Please note, the product name is called "Name" in the Product table, and "ProductName" in the QuoteDetail table. Any help would be much appreciated.

The code used for saving is as follows:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(QuoteViewModel qvm,[Bind(Include = "CustomerId,SalesRep,FirstName,LastName,Company,Address1,Address2,City,State,PostalCode,WorkPhone,CellPhone,Email,Discount,PaymentTerms")] Customer customer,
[Bind(Include = "QuoteId,QuoteDetailId,ProductId,ProductName,Amount,ListPrice,Discount,Price")] List<QuoteDetail> quoteDetails,
[Bind(Include = "QuoteId,CustomerId,Subtotal,Tax,Total,QuoteDate,GoodUntil,QuoteSent,DateApproved,DateOrdered")] Quote quote)

{

if (ModelState.IsValid)
{

foreach (var quoteDetail in qvm.QuoteDetail)
{

QuoteDetail qd = db.QuoteDetails.FirstOrDefault(o => ((o.QuoteId == quoteDetail.QuoteId) && (o.QuoteDetailId == quoteDetail.QuoteDetailId)));

if (qd != null)
{
qd.ProductId = quoteDetail.ProductId;
qd.ProductName = quoteDetail.ProductName;
qd.Amount = quoteDetail.Amount;
qd.ListPrice = quoteDetail.ListPrice;
qd.Discount = quoteDetail.Discount;
qd.Price = quoteDetail.Price;
}
else
{
db.QuoteDetails.Add(quoteDetail);
}

}

quote.QuoteId = qvm.QuoteId;
quote.GoodUntil = qvm.GoodUntil;
quote.Discount = qvm.Discount;
quote.DateApproved = qvm.DateApproved;
quote.DateOrdered = qvm.DateOrdered;
quote.Discount = qvm.Discount;
quote.QuoteDate = qvm.QuoteDate;
quote.QuoteSent = qvm.QuoteSent;
quote.Subtotal = qvm.Subtotal;
quote.Tax = qvm.Tax;
quote.Total = qvm.Total;

customer.CustomerId = qvm.CustomerId;
customer.Address1 = qvm.Address1;
customer.Address2 = qvm.Address2;
customer.CellPhone = qvm.CellPhone;
customer.City = qvm.City;
customer.Company = qvm.Company;
customer.Discount = qvm.Discount;
customer.Email = qvm.Email;
customer.FirstName = qvm.FirstName;
customer.LastName = qvm.LastName;
customer.PaymentTerms = qvm.PaymentTerms;
customer.PostalCode = qvm.PostalCode;
customer.SalesRep = qvm.SalesRep;
customer.State = qvm.State;
customer.WorkPhone = qvm.WorkPhone;

db.Entry(quote).State = EntityState.Modified;
db.Entry(customer).State = EntityState.Modified;

bool saveFailed;
do
{
saveFailed = false;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
var objContext = ((IObjectContextAdapter)db).ObjectContext;
// Get failed entry
var entry = ex.Entries.Single();
// Now call refresh on ObjectContext
objContext.Refresh(RefreshMode.ClientWins, entry.Entity);


}

} while (saveFailed);

return RedirectToAction("Index");
}
return View(qvm);
}
}
}


Revised DropDownListFor:

@Html.DropDownListFor(model => model.ProductName, new SelectList(ViewBag.ProductData, "ProductId", "Name"), new { htmlAttributes = new { name = "ProductId", @id = "ProductName", @class = "ProductList" } });

Answer Source

This is because when you submit a form, values in all of your respective form elements are mapped to the name attribute of the element that contains them and not the id attribute.

You'll need to ensure that you are explicitly setting the name attribute as expected on your elements to make sure that they are bound the proper values:

<!-- If you wanted the value from this bound as "ProductId", you would need to set the
     name attribute in the htmlAttributes object -->
@Html.DropDownListFor(model => model.ProductName, ..., new { ...  name="ProductId" } });

Depending on your needs, it may be easier to simply not make any changes on your own to the DropDownList and allow it to bind directly to your ProductId property and then set your name when your AJAX call has completed:

@Html.DropDownListFor(model => model.ProductId, new SelectList(ViewBag.ProductData, "ProductId", "Name"), new { @class = "ProductList" } });

along with:

$(document).on("change", '[id*="ProductId"]', function () {
     $.post("/QuoteViewModel/GetProduct", { pId: $(this).val() }, function (data) {
         $("[id*='ProductId']").val(data.ProductId);
         // This could be a hidden element within your form
         $("[id*='ProductName']").val(data.ProductName);
         $("[id*='Price']").val(data.Price);
     });
});