How to do Update , Delete process using Code First Approach? - javascript

I have one big doubt. I have "Customer Form". Customer Form contain 22 fields. I have Db for "Customer Form". it Contain"Multiple tables" with "many to many relationship". If I connect my Db with "Vb express 2012" using "Connection Strings" and also created EDMX file.it shows view for each table. so i tried to bring all fields in "Single view" and tried to insert the data into multiple tables. For that I used "Code First Approach" i followed the method same as like which is mentioned in the below link. Its working Fine.
http://www.codeproject.com/Tips/651495/Inserting-Data-into-Multiple-Tables-using-Code-Fir
Now my question is i finished the "insert" process using Code first approach.. Now " how to do UPDATE process using this approach.. Because i did only the insertion process . still i need to do Update , Details, Delete.. How to do that?
Thanks.

add your customers in a foreach lop in a view and pass each ones primary key to the edit and delete methods of the controller as follows:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.s1)
</td>
<td>
#Html.DisplayFor(modelItem => item.s2)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.id })
</td>
</tr>
}
then in your controller make a GET method to accept the primary key as a parameter from the view as follows:
public ActionResult Edit(int id)
{
Class1 class1 = db.Class1.Find(id);
return View(class1);
}
and then make a Form in your Edit View to POST back the values as:
#using (Html.BeginForm())
{
<div class="form-horizontal">
<h4>Class1</h4>
<hr />
#Html.HiddenFor(model => model.id)
<div class="form-group">
#Html.LabelFor(model => model.s1, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.s1, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.s2, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.s2, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
then in your controller make a POST method to accept the model as a parameter from the view as follows:
[HttpPost]
public ActionResult Edit([Bind(Include = "id,s1,s2")] Class1 class1)
{
db.Entry(class1).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
Similarly make GET and POST methods for Delete.

basically in an update method...
Find the object to be updated
-Example your datacontext class object=ctx,tablename=details
update the found object with the new data
if you're not passing an entire object to the method then just pass the primary key of the object to be updated and the new values
public void update(details newData)
{
var result=ctx.details.Find(id);
result.name=newData.Name;
result.age=newData.Age;
ctx.SaveChanges();
}
to Delete:
a similar process
Find the object -Example your datacontext class object=ctx,tablename=details
Remove it
public void Delete(int id)
{
var result=ctx.details.Find(id);
ctx.details.Remove(result);
ctx.SaveChanges();
}

Related

Submit values from razor view to a controller action which accept values by HTTP Post method

I have a view in one of my controller which have list of items. When user clicks each item, browser must navigate to page which brings details about this item.
Controller for Details accepts values through post method and have complex input object as its input.
Here is sample method for to navigate to details page using GET method to send values:
function openDetailsPage(commodityID, commodityName) {
dateFrom = convertNumbers2English('#(((FilterViewModel)ViewBag.ViewModel).dateValue_1)');
dateTo = convertNumbers2English('#(((FilterViewModel)ViewBag.ViewModel).dateValue_2)');
dateFrom = changeDateSeparator(dateFrom);
dateTo = changeDateSeparator(dateTo);
if (dateTo == null || dateTo == undefined)
dateTo = "0";
if (dateFrom == null || dateFrom == undefined)
dateFrom = "0";
#{
string reportTypes = "0";
if (((FilterViewModel)ViewBag.ViewModel).purchaseReportTypes != null)
{
reportTypes = String.Join(",", ((FilterViewModel)ViewBag.ViewModel).purchaseReportTypes);
}
}
alert('#reportTypes');
var url = '#Url.Action("ReportDetailed","BuyReport",new {
commodityType =(((FilterViewModel)ViewBag.ViewModel).commodityType),
commodityName="dummyCommodityName",
department=((FilterViewModel)ViewBag.ViewModel).department,
repository=((FilterViewModel)ViewBag.ViewModel).repository,
commodity ="dummyCommodityID",
purchaseReportTypes=reportTypes,
dateValue_1="dummyDate1",
dateValue_2="dummyDate2"
})';
alert(url);
#*var url = '#Url.Action("ReportDetailed","BuyReport",
new RouteValueDictionary
{
{"commodityType",((FilterViewModel)ViewBag.ViewModel).commodityType},
{"commodityName","dummyCommodityName" },
{"department",((FilterViewModel)ViewBag.ViewModel).department },
{"repository",((FilterViewModel)ViewBag.ViewModel).repository },
{"commodity","dummyCommodityID"},
{"purchaseReportTypes",((FilterViewModel)ViewBag.ViewModel).purchaseReportTypes },
{"dateValue_1",((FilterViewModel)ViewBag.ViewModel).dateValue_1 },
{ "dateValue_2",((FilterViewModel)ViewBag.ViewModel).dateValue_2 }
})';*#
url = url.replace("dummyCommodityID", commodityID);
url = url.replace("dummyCommodityName", commodityName);
url = url.replace("dummyDate1", dateFrom);
url = url.replace("dummyDate2", dateTo);
alert(url);
openLink(url);
}
I have some difficulties with this type of routing for values:
Input object is complex so route would be so complex. E.g. /BuyReport/ReportDetailed?commodityType=0&commodityName=dummyCommodityName&department=1&repository=2&commodity=dummyCommodityID&dateValue_1=dummyDate1&dateValue_2=dummyDate2 or /BuyReport/ReportDetailed/0/itemName/1/2/1/123/
Any special characters in get parameters such as / will break routing
I cannot pass stuff like arrays so I should convert them before sending
So I'm looking for a method to send parameters using 'Post' method like what form submit button does with below constraints:
I have no forms in my view
I want to post values to controller and page must navigate to details view
Each item in first page, have different row and different ID so I think creating a form for each row is not reasonable.
I want to know are there any ways to implement Post parameters according to my requirements? I would not care if it would be a mixture of C#, JS and jQuery.
More Details:
Here is a sample row in my list page which calls openDetailsPage js function:
<a onclick="openDetailsPage(#item.CommodityId,'#Html.DisplayFor(modelItem => item.CommodityName)')">
<div class="ios-content-box px-4 py-1 mb-3 ios-hover-box">
<div class="row font-12 my-2 ios-divider-line">
<div class="col-6 f-w-600 color-orange text-right">#Html.DisplayFor(modelItem => item.CommodityName)</div>
<div class="col-6 text-left"> <i class="fas fa-chevron-left fa-fw color-orange "></i></div>
</div>
<div class="row font-12 my-2 ios-divider-line">
<div class="col-6 text-gray-600 text-right">type</div>
<div class="col-6 text-gray-600 text-left">#Html.DisplayFor(modelItem => item.TypesName)</div>
</div>
<div class="row font-12 my-2 ios-divider-line">
<div class="col-6 text-gray-600 text-right">Code</div>
<div class="col-6 text-gray-600 text-left">#Html.DisplayFor(modelItem => item.CommodityCode)</div>
</div>
<div class="row font-12 my-2 ios-divider-line">
<div class="col-6 text-gray-600 text-right">Barcode</div>
<div class="col-6 text-gray-600 text-left">#Html.DisplayFor(modelItem => item.CommodityBarcode)</div>
</div>
<div class="row font-12 my-2 ios-divider-line">
<div class="col-6 text-gray-600 text-right">Unit Price</div>
<div class="col-6 text-gray-600 text-left">#Html.DisplayFor(modelItem => item.UnitPrice)</div>
</div>
<div class="row font-12 my-2 ios-divider-line">
<div class="col-6 text-gray-600 text-right">Total Price</div>
<div class="col-6 text-gray-600 text-left">#Html.DisplayFor(modelItem => item.SumPrice)</div>
</div>
</div>
</a>
Currently my controller is as below:
[Route("BuyReport/ReportDetailed/{commodityType}/{commodityName}/{department}/{repository}/{commodity}/{purchaseReportTypes}/{dateValue_1}/{dateValue_2}")]
public async Task<ActionResult> ReportDetailed(
string commodityType,
string commodityName,
string department,
string repository,
string commodity,
string purchaseReportTypes,
string dateValue_1,
string dateValue_2
)
{
}
But I want to change it to something like this:
[HttpPost]
public async Task<ActionResult> ReportDetailed(DetailedViewModel detailedviewmodel){
string commodity = detailedviewmodel.commodity;
string commoditytype = detailedviewmodel.commoditytype;
string department = detailedviewmodel.department;
string purchasereporttypes = detailedviewmodel.purchasereporttypes;
string repository = detailedviewmodel.repository;
string startdate = detailedviewmodel.datevalue_1;
string enddate = detailedviewmodel.datevalue_2;
string commdoityname = detailedviewmodel.commodityname;
}
Where DetailedViewModel is defined as below:
public class DetailedViewModel
{
public string commodityType { get; set; }
public string commodityName { get; set; }
public string department { get; set; }
public string repository { get; set; }
public string commodity { get; set; }
public string[] purchaseReportTypes { get; set; }
public string dateValue_1 { get; set; }//start date
public string dateValue_2 { get; set; }//end date
}
This is not the right way to meet your purpose. Your code looks vulnerable for exploiters too. Don't use solutions which break the normal web application behavior.
Instead, send the parameters to the corresponding controller method and then make an internal redirection with model passing (controller side). If your data is stored in database just send CommodityId and find details in controller side instead of sending entire details as form (HTTPPOST). In this way, you have a well designed project without unwanted crashes which come from breaking the behaviors and your code looks simple and clear as you want.
One quick simple solution is to post via Ajax:
Let's imagine this as your controller:
[HttpGet]
public ActionResult ReportDetailed()
{
return View();
}
[HttpPost]
public JsonResult ReportDetailed(DetailedViewModel detailedviewmodel)
{
var status = "error";
var message = "";
try
{
string commodity = detailedviewmodel.commodity;
string commoditytype = detailedviewmodel.commodityType;
string department = detailedviewmodel.department;
List<string> purchasereporttypes = detailedviewmodel.purchaseReportTypes;
string repository = detailedviewmodel.repository;
string startdate = detailedviewmodel.dateValue_2;
string enddate = detailedviewmodel.dateValue_1;
string commdoityname = detailedviewmodel.commodityName;
// your code here ...
status = "success";
return Json(new { status, detailedviewmodel } , JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
message = ex.Message;
return Json(new { status, message }, JsonRequestBehavior.AllowGet);
}
}
Assuming you have defined DetailedViewModel inside the Models folder:
public class DetailedViewModel
{
public string commodityType { get; set; }
public string commodityName { get; set; }
public string department { get; set; }
public string repository { get; set; }
public string commodity { get; set; }
public List<string> purchaseReportTypes { get; set; }
public string dateValue_1 { get; set; }//start date
public string dateValue_2 { get; set; }//end date
}
In your View, I copy the whole Html and Javascript for you, just grab it and tweak it to your needs:
#model Your_Proj_Namespace.Models.DetailedViewModel
#{
ViewBag.Title = "ReportDetailed";
}
<h2>ReportDetailed</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DetailedViewModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.commodityType, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.commodityType, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.commodityType, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.commodityName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.commodityName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.commodityName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.department, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.department, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.department, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<label>purchaseReportTypes: 3 inputs for example</label>
<div class="col-md-10">
<input type="text" name="purchaseReportTypes[0]" class="form-control inputPurchaseReportTypes " value="" />
<input type="text" name="purchaseReportTypes[1]" class="form-control inputPurchaseReportTypes " value="" />
<input type="text" name="purchaseReportTypes[2]" class="form-control inputPurchaseReportTypes " value="" />
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.repository, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.repository, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.repository, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.commodity, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.commodity, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.commodity, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.dateValue_1, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.dateValue_1, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.dateValue_1, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.dateValue_2, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.dateValue_2, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.dateValue_2, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" id="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
#section scripts{
<script>
$('#submit').click(function (e) {
e.preventDefault();
alert('form submitted');
var list = [];
$('.inputPurchaseReportTypes').each( function (key, value) {
list.push($(this).val());
});
const DATA = {
commodityType: $('#commodityType').val(),
commodityName: $('#commodityName').val(),
department: $('#department').val(),
repository: $('#repository').val(),
commodity: $('#commodity').val(),
purchaseReportTypes: list,
dateValue_1: $('#dateValue_1').val(),
dateValue_2: $('#dateValue_2').val(),
};
console.log(DATA);
$.ajax({
url: '/YourControllerName/ReportDetailed',
type: "POST",
contentType: "application/json",
dataType: "json",
data: JSON.stringify(DATA),
success: function (result) {
alert('success');
console.log(result);
// your code here
}
});
});
</script>
}
If you prefer not to use Ajax, comment the javascript code above (all the code inside <script>), to post the form directly.
I built and debugged the above code. Feel free to test it.
Hope this helped.
Finally, You might find the following links useful:
https://www.tutorialsteacher.com/mvc/model-binding-in-asp.net-mvc
This link clearly shows how to handle the list of Authors inside a book viewmodel, similar to yours: http://codebuckets.com/2016/09/07/asp-net-mvc-and-binding-complex-objects-magic/
https://stackoverflow.com/a/16326290/4687359
I agree with the solution by #A. Nadjar
one more note
Use HttpGet if you want user to share the url and show same data as he see it to another user ,
if not? use HttpPost with one Object parameter,
because maybe there's a Nullable parameters the user won't search by so the url will be like this
BuyReport/ReportDetailed/dummyCommodityName/null/null/null/dummyCommodityID/null/2/0
or don't use this custom route [Route("BuyReport/ReportDetailed/{commodityType}/{commodityName}/{department}/{repository}/{commodity}/{purchaseReportTypes}/{dateValue_1}/{dateValue_2}")]
so he can use the Query String and pass only the keys => values he want

Adding a list of complex data models to form

I'm using Entity Framework 7 with ASP.NET MVC 5.
I have some forms that look like this. Clicking on one of the "new" buttons brings up a Bootstrap modal that looks like this. Submitting the modal form adds a new entity to the database before appending its name and primary key to the selectlist.
This works, but if the user changes their mind, the item(s) created via the modal (location in this case) stick around forever. So ideally none of the child items would be created until the main form is finished. While the example only has two simple fields, other data models have more than half a dozen, which may include complex fields of their own (but preventing that wouldn't be a horrible restriction).
So what's the best way to do this, nested divs serialized by JavaScript? Nested divs would also make it easy to allow reordering by the user, which is the end goal.
Does ASP.NET have a better way to handle this?
This feels hacky, but it works.
Using BeginCollectionItem, you can have the modal add hidden input elements to the DOM.
I wrote an action method that returns JSON with the modelstate (valid/invalid) and errors or partial HTML for invalid and valid submissions, respectively. Based on this, JavaScript either adds the errors to the summary or adds the requisite label and hidden inputs to the initial form.
Then have the main form's viewmodel contain an ICollection of your data model, called Contacts in below code, and ASP.NET handles the data binding with no troubles.
Example:
_CollectionItem.cshtml (partial HTML added to main form after valid submission)
#model Project.Models.ContactCreateViewModel
<li>
<div class="collection-item">
#using (Html.BeginCollectionItem("Contacts"))
{
<span class="item-name">#Model.LastName, #Model.FirstName</span> <span class="btn btn-danger delete-item">Delete</span>
#Html.HiddenFor(model => model.FirstName)
#Html.HiddenFor(model => model.LastName)
#Html.HiddenFor(model => model.PhoneNumber)
#Html.HiddenFor(model => model.PhoneExt)
#Html.HiddenFor(model => model.Email)
}
</div>
</li>
_CreateModal.cshtml (partial used for the body of the modal)
#model Project.Models.ContactCreateViewModel
<div class="modal-header">
<button type="button" class="close btn-modal-close" data-dismiss="modal"><i class="fas fa-times"></i></button>
<h4 class="modal-title">New Contact</h4>
</div>
<div class="modal-body">
#using (Html.BeginForm("CreateModal", "Contacts", FormMethod.Post, new { id = "new-contact-form" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummaryPlaceholder()
#* First Name *#
<div class="form-group">
#Html.LabelFor(model => model.FirstName, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-8">
#Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
</div>
</div>
#* Last Name *#
<div class="form-group">
#Html.LabelFor(model => model.LastName, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-8">
#Html.EditorFor(model => model.LastName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LastName, "", new { #class = "text-danger" })
</div>
</div>
#* Phone Number *#
<div class="form-group">
#Html.LabelFor(model => model.PhoneNumber, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-8">
#Html.EditorFor(model => model.PhoneNumber, new { htmlAttributes = new { #class = "form-control phone" } })
#Html.ValidationMessageFor(model => model.PhoneNumber, "", new { #class = "text-danger" })
</div>
</div>
#* Phone Ext *#
<div class="form-group">
#Html.LabelFor(model => model.PhoneExt, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-8">
#Html.EditorFor(model => model.PhoneExt, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PhoneExt, "", new { #class = "text-danger" })
</div>
</div>
#* Email *#
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-8">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</div>
</div>
#* SUBMIT *#
<div class="form-group">
<div class="col-md-offset-4 col-md-8">
<input type="submit" value="Create" class="btn btn-success" />
</div>
</div>
</div>
}
</div>
#Scripts.Render("~/Scripts/Custom/ajax-add-collection-item.js")
<script>
$(function () {
ajaxAddCollectionItem("new-contact-form", "contacts", function () {
alertify.success("Added new contact");
})
});
</script>
ajax-add-collection-item.js (capture modal form submission, add _CollectionItem.cshtml to main form)
// Posts form and adds collection item to ul
function ajaxAddCollectionItem(formId, listId, onSuccess = function () { }) {
let $form = $("#" + formId);
$form.submit(function (event) {
event.preventDefault();
$.ajax({
method: "POST",
url: $form.attr("action"),
data: $form.serialize(),
success: function (data) {
let successful = data["success"];
// If form is valid, close modal and append new entry to list
if (successful) {
$("#" + listId).append(data["html"]);
$(".delete-item").click(function (event) {
$(this).closest("li").remove();
});
$(".btn-modal-close").trigger("click");
onSuccess();
}
// If form is not valid, display error messages
else {
displayValidationErrors(data["errors"]);
}
},
error: function (error) {
alert("Dynamic content load failed.");
console.error("Ajax call failed for form: " + $form);
}
});
});
// Populate validation summary
function displayValidationErrors(errors) {
let $ul = $('div.validation-summary-valid.text-danger > ul');
$ul.empty();
$.each(errors, function (i, errorMessage) {
$ul.append('<li>' + errorMessage + '</li>');
});
}
}
ContactsController.cs
public class ContactsController
{
// GET: Contacts/CreateModal
public ActionResult CreateModal()
{
return PartialView("_CreateModal");
}
// POST: Contacts/CreateModal
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> CreateModal(ContactCreateViewModel viewModel)
{
// If form is valid and email does not already exist, send HTML for collection item,
// otherwise send modelstate errors
if (ModelState.IsValid)
{
User user = await UserManager.FindByEmailAsync(viewModel.Email);
if (user == null)
// RenderPartialView returns partial HTML as a string,
// see https://weblog.west-wind.com/posts/2012/May/30/Rendering-ASPNET-MVC-Views-to-String
return Json(new { success = true, html = RenderPartialView("_CollectionItem", viewModel) });
else
ModelState.AddModelError("Email", "Email already exists.");
}
return Json(new { success = false, errors = GetModelStateErrors() });
}
// Actually in base controller class
protected string[] GetModelStateErrors()
{
return ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage).ToArray();
}
}

using jQuery UI Datepicker in MVC razor getting error

I am following this example to use jQuery UI Datepicker in my MVC project.
Model
[DataType(DataType.Date)]
public DateTime RequestDate { get; set; }
Razor
<div class="form-group">
#Html.LabelFor(model => model.RequestDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.RequestDate)
#Html.ValidationMessageFor(model => model.RequestDate, "", new { #class = "text-danger" })
</div>
Then as per tutorial created Date helper and pasted this line
#Html.TextBox("", String.Format("{0:d}", (string)Model.ToShortDateString()), new { #class = "datefield", #type = "date" })
When i run the application, i get run time exception for above line. Model is null. I am not sure why? and How does it bind to my model?
Additional information: Cannot perform runtime binding on a null
reference
DatePickerReady.js
if (!Modernizr.inputtypes.date) {
$(function () {
$(".datefield").datepicker();
});
}
I think your problem is caused by the casting part of (string)Model.ToShortDateString(), no need for the casting here as the ToShortDateString will return a string anyways. Try this instead:
#Html.TextBox("", String.Format("{0:d}", Model.ToShortDateString()),
new { #class = "datefield", #type = "date" })
And you need to make sure to name your editor template a name matching your data type (DateTime.cshtml in your case) or use the UIHint attribute if you like to customize the name, and make sure you add your template under the proper EditorTemplates folder.

DataBinding: 'System.String' does not contain a property with the name 'numeroGuia'

I am a newbie at ASP.net MVC5, my problem is this:
I am creating a partial view "AgregaGuia", in which I make a query to TblGuias model of a row that do not yet have "fechaRecepcionGuia", these guides are filled in a combobox and when selected this the guide fills all textbox in that view. However when running the application it generated the following error: DataBinding : 'System.String' does not Contain a property with the name 'numeroGuia'.
Could anyone help me please??
this is my model:
public partial class TblGuias
{
public TblGuias()
{
this.TblFactIC = new HashSet<TblFactIC>();
}
public string numeroGuia { get; set; }
public string companiaEnvios { get; set; }
public string destino { get; set; }
public decimal pesoGuia { get; set; }
public System.DateTime fechaEnvioGuia { get; set; }
public Nullable<System.DateTime> fechaRecepcionGuia { get; set; }
public string comprobante { get; set; }
public virtual ICollection<TblFactIC> TblFactIC { get; set; }
}
this is my controller:
public class vueInveEntrsController : Controller
{
public ActionResult AgregaGuia()
{
ViewData["guia"] = new SelectList(db.TblGuias.Where(g => g.fechaRecepcionGuia == null).Select((g => g.numeroGuia)),"numeroGuia", "companiaEnvios","destino","pesoGuia","fechaEnvioGuia");
return PartialView(db.TblGuias.ToList());
}
[HttpPost]
public ActionResult Action(string numero)
{
var query = from c in db.TblGuias
where c.numeroGuia == numero
select c;
return Json(query);
}
}
and my view is as follows:
#using (#Html.BeginForm("Action", "vueInveEntrs", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-group">
#Html.Label("Seleccione Guia", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("numero", (SelectList)ViewData["guia"], new { onchange = "Action(this.value);", #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.Label("CompaƱia Envios", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBox("transporte", null, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.Label("Destino", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBox("destino", null, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.Label("Peso", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBox("peso", null, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.Label("Fecha Envio", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBox("fechaenvio", null, new { #class = "form-control" })
</div>
</div>
}
<script type="text/javascript">
function Action(numero) {
$.ajax({
url: '#Url.Action("Action", "vueInveEntrs")',
type: "POST",
data: { "numero": numero },
"success": function (data) {
if (data != null) {
var vdata = data;
$("#transporte").val(vdata[0].companiaEnvios);
$("#destino").val(vdata[0].destino);
$("#peso").val(vdata[0].pesoGuia);
$("#fechaenvio").val(vdata[0].fechaEnvioGuia);
}
}
});
}
</script>
The problem is this line in your controller:
ViewData["guia"] = new SelectList(
db.TblGuias.Where(g => g.fechaRecepcionGuia == null).Select((g => g.numeroGuia)),
"numeroGuia", "companiaEnvios","destino","pesoGuia","fechaEnvioGuia");
You are not specifying the constructor parameters for SelectList properly. There are several different overloads, but I think the one you want is this one:
public SelectList(
IEnumerable items,
string dataValueField,
string dataTextField
)
The first parameter, items, represents the list of items that you want be rendered into <option> tags inside the <select>.
The second parameter, dataValueField, is the name of the property on the items in the enumerable which will become the value attribute inside each <option> tag.
Similarly, the third parameter, dataTextField, is the name of the property which will become the text displayed for each <option>.
So, if you change your code to the following, I think it should work:
ViewData["guia"] = new SelectList(
db.TblGuias.Where(g => g.fechaRecepcionGuia == null), "numeroGuia", "numeroGuia");
If you want different text to display in the dropdown list, change the third parameter to a different property from your TblGuias class.

ASP.NET MVC jQuery Ajax - close and refresh parent table from modal dialog

Very new to both MVC and jQuery, so I'm not sure how to get this to work. I've cobbled together a (somewhat) working modal dialog form with an ajax postback. Been searching for two days and not finding great MVC+jQuery examples out there. The data gets inserted as expected, it's just the UI I'm having a hard time with.
I've got two views: Index and Create. Index lists all records inside a plain table, looping the results with Razor. Create is the insert form which I'm loading into a modal dialog:
#model IEnumerable<MyProject.Models.StockIndex>
#{
ViewBag.Title = "Admin: Stock Index Home";
Layout = "~/Views/Shared/_LayoutAdmin.cshtml";
}
<h2>View Stock Indices</h2>
<p>
Create New
<div id="createStockIndexForm"></div>
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Description)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
#Html.ActionLink("Details", "Details", new { id = item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Id })
</td>
</tr>
}
</table>
#section Scripts {
<script>
$('#createLink').on('click', function () {
$("#createStockIndexForm").dialog({
autoOpen: true,
position: { my: "center", at: "top+350", of: window },
width: 600,
resizable: false,
title: 'Create Stock Index',
modal: true,
open: function () {
$(this).load('#Url.Action("Create", "AdminStockIndex")');
}
});
return false;
});
</script>
}
Controller actions:
public ActionResult Create()
{
var model = new StockIndexEditViewModel()
{
StockIndices = GetIndices()
};
return View(model);
}
[HttpPost]
public ActionResult Create(StockIndexEditViewModel model)
{
if (ModelState.IsValid)
{
...
}
return PartialView(model);
}
The "Create" form that was loaded into the dialog (above):
#model MyProject.Models.StockIndexEditViewModel
#{
ViewBag.Title = "CreatePartial";
Layout = "~/Views/Shared/_LayoutAdmin.cshtml";
}
<h2>CreatePartial</h2>
#using (Html.BeginForm("Create", "AdminStockIndex", FormMethod.Post, new { id = "createForm" }))
{
#Html.AntiForgeryToken()
<div id="result"></div>
<div class="form-horizontal">
<h4>StockIndexEditViewModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.SelectedParentId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SelectedParentId, Model.StockIndices, "- Select One -", new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" id="createButton" class="btn btn-default" />
</div>
</div>
</div>
}
#section Scripts {
<script>
$("#createForm").on("submit", function (event) {
event.preventDefault();
$.ajax({
url: this.action,
type: this.method,
async: true,
data: $(this).serialize(),
success: function (data) {
if (data) {
var createForm = $("#createStockIndexForm").dialog();
createForm.dialog("close");
}
else {
$("#result").append("Something went fail.");
}
}
});
});
</script>
}
The modal dialog always goes blank, rather than closing. In testing in Firefox using Firebug, I occasionally see this error, but not every time:
InvalidAccessError: A parameter or an operation is not supported by
the underlying object
Upon searching, I see that it's a CORS problem where FF is enforcing the rules and other browsers may not be. It's frustrating that the error doesn't always occur - appears to be random. Behaves the same in Chrome but doesn't ever throw an error in the JS console.
Firstly, how would I pull this off? Secondly, is there a way to refresh the table on the parent page via ajax, without plugins?
UPDATE:
With Eckert's help, I've made a little progress.
I'm trying to avoid the MVC Ajax helpers and sticking with a "pure" jQuery approach. I replaced the list of records on Index with a div, which contains a PartialView:
<div id="stockIndices">
#Html.Partial("_StockIndices", Model)
</div>
I've got the underlying table refresh working by reloading the div with the close property of the jQuery dialog:
$('#createLink').on('click', function () {
$("#createStockIndexForm").dialog({
autoOpen: true,
position: { my: "center", at: "top+400", of: window },
width: 600,
resizable: false,
title: 'Create Stock Index',
modal: true,
open: function () {
$(this).load('#Url.Action("Create", "AdminStockIndex")');
},
close: function () {
$("#stockIndices").load('#Url.Action("GetStockIndices", "AdminStockIndex")');
}
});
return false;
});
Upon closing the modal dialog manually, the div reloads just how I wanted. Great! Now if I could get the dialog to close when the form posts, I'd be set. This does not work:
$("#createStockIndexForm").dialog("close");
Firebug reports the error:
Error: cannot call methods on dialog prior to initialization;
attempted to call method 'close'
The modal dialog always goes blank, rather than closing.
It's probably behaving improperly because you're creating a variable based on the dialog method of an object, and not the object itself. Try this instead:
if (data) {
$("#createStockIndexForm").dialog("close");
}
Secondly, is there a way to refresh the table on the parent page via
ajax, without plugins?
There sure is, but it may require you to change some things, including the code to close your dialog. Here's how I would handle it:
1) your table of records should be a partial view within the main Index view. The reason for this is because when we submit your form, we'll use the ajax-option "InsertionMode" coupled with the target-id to replace your old records table with the updated one from the form.
2) rather than handling your dialog-close code in the on-submit method, we'll instead use the ajax-option "OnSuccess" to do that (and also "OnFailure" to handle your 'Something went fail' error).
So here's your new Index view:
#model IEnumerable<MyProject.Models.StockIndex>
#{
ViewBag.Title = "Admin: Stock Index Home";
Layout = "~/Views/Shared/_LayoutAdmin.cshtml";
}
<h2>View Stock Indices</h2>
<p>
Create New
<div id="createStockIndexForm"></div>
</p>
<div id="recordsDiv">
#Html.Partial("RecordsPartial", model)
</div>
// all your script stuff can still go at the end
Most of the Index view is unchanged, with the exception that we're now using a that contains a partial view. This partial view will include the table of records, coded here:
Create a new partial view named "RecordsPartial":
#model IEnumerable<MyProject.Models.StockIndex>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Description)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
#Html.ActionLink("Details", "Details", new { id = item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Id })
</td>
</tr>
}
Now your "Create" view will be updated to use the mvc-ajax helper rather than use all that additional javascript code:
#model MyProject.Models.StockIndexEditViewModel
#{
ViewBag.Title = "CreatePartial";
Layout = "~/Views/Shared/_LayoutAdmin.cshtml";
}
<h2>CreatePartial</h2>
#using (Ajax.BeginForm("CreateRecord", "AdminStockIndex", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "recordsDiv", OnSuccess = "postSuccess", OnFailure = "postFailed" })
{
#Html.AntiForgeryToken()
<div id="result"></div>
<div class="form-horizontal">
<h4>StockIndexEditViewModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
/* form fields here */
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" id="createButton" class="btn btn-default" />
</div>
</div>
</div>
}
We changed your form to be ajax-post, and we've added ajax-options to handle what happens after your form has posted. The data that returns after the post (our updated records partial) will replace the current contents of the target-id "recordsDiv". OnSuccess function "postSuccess" will handle closing the dialog box. OnFailure function "postFailed" will report that something bad happened. The last thing to mention is that we changed the post-action from "Create" to "CreateRecord". I prefer to have unique action names when working with ajax-data returns. It's just a cleaner approach.
Before we define the new "CreateRecord" post-action, we need to implement our success and failure functions. Just create them at the bottom of your script-section block in the main "Index" view:
#section Scripts {
<script>
// ... other stuff that was already here ...
function postSuccess() {
$("#createStockIndexForm").dialog("close");
}
function postFailed() {
alert("Failed to post"); // you can define your own error
}
</script>
}
Lastly, we create the "CreateRecord" post-action, which will process our form and return an updated "records partial" view:
[HttpPost]
public ActionResult CreateRecord(StockIndexEditViewModel model)
{
if (ModelState.IsValid)
{
... create record here ...
}
var records = db.Indexes.ToList(); // or whatever your table name is
return PartialView("RecordsPartial", records);
}
This is repeating some of your existing "Create" action. We simply process our post data, then we get a new updated list of records, and lastly we return that list of records back to our "RecordsPartial" view, which will be inserted into our "recordsDiv" as we specified in our ajax-options.
Very clean and simple solution. If you have questions, feel free to ask.
In your main Index view, rather than calling for your Create view to be inserted into your view, have it initially present at view load, and hide it within a div:
<div id="createStockIndexForm">
#Html.Action("Create", "AdminStockIndex")
</div>
In your Index script section, we're creating the dialog OUTSIDE your click event. We're also turning the "autoOpen" value to false so the div hides at view load.
Index script section:
#section Scripts {
<script>
$("#createStockIndexForm").dialog({
autoOpen: false,
position: { my: "center", at: "top+350", of: window },
width: 600,
resizable: false,
title: 'Create Stock Index',
modal: true
});
$('#createLink').on('click', function () {
$("#createStockIndexForm").show();
});
</script>
}
Also, when you use the PreventDefault() command, it appears to be interfering with your modal-close command (after some of my own testing). I suggest that you change your form's "Create" button to type="button" rather than "submit", and then use the ID of the button to handle your ajax-post method.
<input type="button" id="createButton" value="Create" class="btn btn-default" />
Add this to your main Index script section at the bottom:
$("#createButton").on("click", function () {
$.ajax({
url: this.action,
type: this.method,
async: true,
data: $(this).serialize(),
success: function (data) {
if (data) {
$("#createStockIndexForm").dialog("close");
}
else {
$("#result").append("Something went fail.");
}
}
});
});
Make sure to direct your "close" command at the dialog object itself.
Here's a fiddle I created that shows you the gist of how it should be:
http://jsfiddle.net/ch5aLezg/
So to recap on the script stuff, you should have NO SCRIPTS in your "Create" partial. All the scripts go into the main Index view, as I've detailed in this answer.
I found a way to get everything I wanted, while retaining the "pure" jQuery Ajax approach, instead of resorting to the MVC Ajax helpers, suggested by Eckert. His suggestions lead me to the solution, however. Big thanks!
I created a PartialView in the controller:
public ActionResult GetStockIndices()
{
_service = new StockIndexService();
var data = _service.GetAll();
return PartialView("_StockIndices", data);
}
...and its view:
#model IEnumerable<MyApp.Models.StockIndex>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Description)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
#Html.ActionLink("Details", "Details", new { id = item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Id })
</td>
</tr>
}
</table>
Then I changed the modal dialog script to load the partial view when closed. Here's the entire Index view, for posterity:
#model IEnumerable<MyApp.Models.StockIndex>
#{
ViewBag.Title = "Admin: Stock Index Home";
Layout = "~/Views/Shared/_LayoutAdmin.cshtml";
}
<h2>View Stock Indices</h2>
<p>
Create New
<div id="createStockIndexForm"></div>
</p>
<div id="stockIndices">
#Html.Partial("_StockIndices", Model)
</div>
#section Scripts {
<script>
var _dialog;
$('#createLink').on('click', function () {
_dialog = $("#createStockIndexForm").dialog({
autoOpen: true,
position: { my: "center", at: "top+400", of: window },
width: 600,
resizable: false,
title: 'Create Stock Index',
modal: true,
open: function () {
$(this).load('#Url.Action("Create", "AdminStockIndex")');
},
close: function () {
$("#stockIndices").load('#Url.Action("GetStockIndices", "AdminStockIndex")');
}
});
return false;
});
</script>
}
Notice the global "_dialog" variable. This gave me access to the dialog from the Create form, so it could be closed:
<script>
$("#createForm").on("submit", function (event) {
event.preventDefault();
$.ajax({
url: this.action,
type: this.method,
async: true,
data: $(this).serialize(),
success: function (data) {
if (data) {
if (_dialog) {
_dialog.dialog("close");
}
}
else {
$("#result").append("Error! Record could not be added.");
}
},
error: function (error) {
console.error(error);
}
});
});
</script>

Categories

Resources