i try to confirm a sale and i need sum information about the product..
my View
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Card</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CardModel.SerialNumber, "Serial No")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CardModel.SerialNumber, new { id = "sn" })
#Html.ValidationMessageFor(model => model.CardModel.SerialNumber)
</div>
<p>
<input type="submit" value="CardSale" id="btnSubmit"
onclick="if (confirm('The owner dealer price is : **#Model.OwnerPrice** . Are you sure?')) { return true; } else { return false; }" />
</p>
</fieldset>
}
i tryed with the ' $.getJSON ' like this:
<script type="text/javascript">
$(document).ready(function()
{
$("#btnSubmit").click(function ()
{
var serial = $("#sn");
var price = "";
var url = "";
url = "#Url.Action("GetOwnerPrice","Card")/"+serial;
$.getJSON(url,function(data)
{
alert(data.Value);
});
});
});
and on the controller
public ActionResult GetOwnerPrice(int sn)
{
var owner = db.Dealers.Single(s => s.DealerID == db.Dealers.Single(o => o.UserName == User.Identity.Name).OwnerDealerID);
var ownerPrice = owner.ProductToSale.Single(pr => pr.ProductID == sn).SalePrice;
return Json(ownerPrice, JsonRequestBehavior.AllowGet);
}
but i dont know how to return it to the onclick confirm msg or into my ViewModel..
any help?
The way in which you have written it, the #Model.OwnerPrice value must be known during page generation, since the template engine only has values that exist in your model when it does the template-data merge.
If you do know this value during page load, then simply use the value in your model exactly as you have, and if the user ever gets to this dialog, they will see the proper value.
If the value of this confirmation dialog is not known during page load, then you have the right idea to retrieve it via an Ajax call. The trick is to update the DOM with the new information when the call finishes. You can do this in three steps. First, change your dialog box:
First:
if (confirm('The owner dealer price is : ' + ownerDealerPrice + '. Are you sure?'))
Second:
Declare a new global variable:
var ownerDealerPrice;
Third:
Get the price:
$.ajax({ url: "/GetOwnerPrice", type: "GET", data: "serial=" + serial })
.success(function (price) {ownerDealerPrice = price; }
});
finnaly i take the two answare =)
my view:
#model oCc.IPToGo.ViewModel.CardSaleViewModel
#{
ViewBag.Title = "Card Sale";
var ownerDealerPrice = 0;
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Card</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CardModel.SerialNumber, "Serial No")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CardModel.SerialNumber)
#Html.ValidationMessageFor(model => model.CardModel.SerialNumber)
</div>
<p>
<input type="submit" value="CardSale" />
</p>
</fieldset>
}
<script type="text/javascript">
$(document).submit(function()
{
var serial = "";
serial = $("#CardModel_SerialNumber").val();
var uurl = "";
uurl = "#Url.Action("GetOwnerPrice","Card")/"+serial;
$.ajaxSetup({ cache: false });
$.ajax({ async: false , url: uurl, dataype : 'json', method : 'GET'})
.success(function (price) {ownerDealerPrice = price;
$.ajaxSetup({ cache: true });
});
return (confirm('The owner dealer price is : ' + ownerDealerPrice + '. Are you sure?'))
});
the controller code
public ActionResult GetOwnerPrice(string ID)
{
var currentDealer = db.Dealers.Single(o => o.UserName == User.Identity.Name);
var owner = db.Dealers.Single(s => s.DealerID ==currentDealer.OwnerDealerID);
var card = db.Cards.Single(s => s.SerialNumber == ID);
var ownerPrice = owner.ProductToSale.Single(pr => pr.ProductID == card.ProductID).SalePrice;
return Json(ownerPrice, JsonRequestBehavior.AllowGet);
}
Did you try $.ajax with async = false?
It will return control right into your click handler. Something like:
var tmp = 0;
$.ajax({
async = false,
url = ...,
dataype = 'json',
method = 'POST'})
.complete(function(data) {
tmp = data
});
if(confirm("my message here: " + tmp)) {... }
Related
The issue I am facing is similar to this old post cascading dropdown for dynamically added row. I am using the "BeginCollectionItemCore" NuGet package to setup a set of partial views, so I have unique Id's for my controls. The problem is, I can only get the first Partial View to respond to the dropdownlist changes. The subsequent dropdownlists won't cascade. I have tried with the scripts in the partial and in the main view but both have the same end result, only the first partial will cascade. Here is my code...
Main View HTML:
#model IEnumerable<RCRTCWA.DATA.DAL.tbl_RCRTimeCards>
#{
ViewBag.Title = "Index";
}
<h2>Time Card</h2>
#using (Html.BeginForm())
{
<div id="TimeCardLines">
#foreach (var item in Model)
{
Html.RenderPartial("_TimeCardRow", item);
}
</div>
}
#Html.ActionLink("Add more time...", "_TimeCardRow", new { ViewContext.FormContext.FormId }, new { id = "addTime"})
Main View Script:
<script>
$(document).ready(function () {
debugger;
$("#addTime").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) { $("#TimeCardLines").append(html); }
});
return false;
});
//var index = "";
$(".timecardrow").focusin(function () {
var ti = $(this).find("[name='timecardrows.index']");
var index = ti.val();
$("#timecardrows_" + index + "__HRS_EndTime").timepicker({
defaultTime: 'now',
minTime: '6:00am',
maxTime: '7:00pm'
});
$("#timecardrows_" + index + "__HRS_StartTime").timepicker({
defaultTime: 'now',
minTime: '6:00am',
maxTime: '7:00pm'
});
//$("#.Line_TimeCard").ajaxSuccess(function () {
// $.getJSON("/TimeCard/AddTimeCardRow/", $("#.Success").html(data).show());
//});
$("#timecardrows_" + index + "__WOTicketNo").change(function () {
var tktid = $(this).val();
$("#timecardrows_" + index + "__WOTicketRepLineNo").empty();
$.ajax({
url: "/TimeCard/GetRepairLines/",
data: { ticket: tktid },
cache: false,
type: "POST",
success: function (data) {
$.each(data, function (i, data) {
$("#timecardrows_" + index + "__WOTicketRepLineNo").append('<option value="' + data.Value + '">' + data.Text + '</option>');
});
},
error: function (response) {
alert("Error : " + response);
}
});
GetCarNumber(tktid);
});
$("#timecardrows_" + index + "__WOTicketRepLineNo").change(function () {
var line = $("#timecardrows_" + index + "__WOTicketRepLineNo").val();
$("#timecardrows_" + index + "__WOTicketLaborLineNo").empty();
$.ajax({
url: "/TimeCard/GetLineDetails/",
data: { lineid: line },
cache: false,
type: "POST",
success: function (data) {
$.each(data, function (i, data) {
$("#timecardrows_" + index + "__WOTicketLaborLineNo").append('<option value="' + data.Value + '">' + data.Text + '</option>');
});
},
error: function (response) {
alert("error : " + response);
}
});
return false;
}).change();
function GetCarNumber(ticket) {
$.ajax({
url: "/TimeCard/GetCarNumber/",
data: { ticket: ticket },
cache: false,
type: "POST",
success: function (data) {
$("#timecardrows_" + index + "carNo").html(data).show();
},
error: function (response) {
alert("Error : " + response);
}
});
}
});
});
</script>
Partial View HTML:
#using HtmlHelpers.BeginCollectionItem
#model RCRTCWA.DATA.DAL.tbl_RCRTimeCards
<div class="timecardrow">
#using (Html.BeginCollectionItem("timecardrows"))
{
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<table>
<tr>
<th class="col-sm-1">
Ticket Number
</th>
<th class="col-sm-1">
Car Number
</th>
<th class="col-sm-1">
Repair Line / Description
</th>
<th class="col-sm-1">
Labor Line / Description
</th>
<th class="col-sm-1">
Start Time
</th>
<th class="col-sm-1">
End Time
</th>
<th class="col-sm-1">
Line Complete?
</th>
</tr>
<tr>
<td class="form-group">
<div class="col-sm-1 tickets">
#Html.DropDownListFor(model => model.WOTicketNo, (SelectList)ViewData["TicketsList"], "Select one...", new { #class = "ticketddl" } )
#Html.ValidationMessageFor(model => model.WOTicketNo, "", new { #class = "text-danger" })
</div>
</td>
<td class="form-group">
<div class="col-sm-1 cars">
<div id="carNo"></div>
#Html.HiddenFor(model => model.CarNo)
#Html.ValidationMessageFor(model => model.CarNo, "", new { #class = "text-danger" })
</div>
</td>
<td class="form-group">
<div class="col-sm-1 replines">
#Html.DropDownListFor(model => model.WOTicketRepLineNo, new SelectList(string.Empty, "Value", "Text"), "Select one...", new { #class = "repairddl" })
</div>
</td>
<td class="form-group">
<div class="col-sm-1 laborlines">
#Html.DropDownListFor(model => model.WOTicketLaborLineNo, new SelectList(string.Empty, "Value", "Text"), "Select one...", new { #class = "lablineddl" })
</div>
</td>
<td class="form-group">
<div class="col-sm-1 starttime">
#Html.EditorFor(model => model.HRS_StartTime, new { #class = "start" })
#Html.ValidationMessageFor(model => model.HRS_StartTime, "", new { #class = "text-danger" })
</div>
</td>
<td class="form-group">
<div class="col-sm-1 endtime">
#Html.EditorFor(model => model.HRS_EndTime, new { #class = "end" })
#Html.ValidationMessageFor(model => model.HRS_EndTime, "", new { #class = "text-danger" })
</div>
</td>
<td class="form-group">
<div class="col-sm-1 completed">
#Html.EditorFor(model => model.Completed)
#Html.ValidationMessageFor(model => model.Completed, "", new { #class = "text-danger" })
</div>
</td>
#*<td class="form-group">
<div class="col-sm-1">
<input type="submit" value="Submit Line" class="btn btn-default" />
</div>
<div id="success" class="alert-danger">
</div>
</td>*#
</tr>
</table>
</div>
}
</div>
Currently I have the script in the Main View, and have attempted to get the index part of the partial view's controls when the user interacts with the partial view. I need a better way to handle this, and need to get the cascading dropdownlist's working properly.
I would prefer to have the script in the Main View (if possible) to keep things simpler.
You do not need to bind the change event handler on dropdown using concatenated id strings, which is prone to errors (typos etc) and not easily readable/maintainable. For your cascading dropdown scenario, all you care about is updating the second select element in the same row. jQuery has some handy methods like closest and find which will make our life easier.
For making it easier for future readers, I am going to assume that your first SELECT element is to render a list of countries and has a css class "countrySelect" and second one is for the states of selected country and has the css class "statesSelect" and both are in the same table row (<tr>).
When you bind the change event, make sure you use jQuery on to do so. This will enable the binding for current and future elements in the DOM .
$("#TimeCardLines").on("change","SELECT.countrySelect",function (e) {
var _this = $(this);
var v = _this.val();
// to do :Change below url variable value as needed for your code
var urlToGetSecondDropDownData = "/Home/GetStates?countryId"+v;
$.getJSON(urlToGetSecondDropDownData,
function (res) {
var items = "";
$.each(res,
function(index, item) {
items += "<option value='" + item.Value + "'>"
+ item.Text + "</option>";
});
_this.closest("tr") // Get the same table row
.find("SELECT.statesSelect") // Find the second dropdown
.html(items); // update the content of it
});
});
Assuming you have a GetStates action method which accepts a countryId param and return the corresponding states as a list of SelectListItem ( the data needed to build the second dropdown). Something like below,
public ActionResult GetStates(int countryId)
{
var states =db.States.Where(f => f.CountryId == countryId)
.Select(f => new SelectListItem() { Value = f.Id.ToString(),
Text = f.Name})
.ToList();
return Json(states, JsonRequestBehavior.AllowGet);
}
//first dropdowlist change event
$("#countryId").change(function () {
var id = this.value;
if (id == "") {
id = "0";
}
$.get('/Home/GetStates/' + id,
function (data) {
$('#stateId').find('option').remove()
$(data).each(
function (index, item) {
$('#stateId').append('<option value="' + item.Value + '">' + item.Text + '</option>')
});
}
);
});
public ActionResult GetStates(int id)
{
List<SelectListItem> StatesList= new List<SelectListItem>();
StatesList.Add(new SelectListItem { Text = "---Select State---", Value = "0" });
var getStateCollection= (from f in _ent.States
where f.CountryId == id && f.DeletedDate == null
select new { f.Id, f.Name}).ToList();
foreach (var item in getStateCollection)
{
StatesList.Add(new SelectListItem { Text = item.Name.ToString(), Value = item.Id.ToString() });
}
return Json(StatesList, JsonRequestBehavior.AllowGet);
}
In the create view what i am trying to do is when you choose a name from the dropdown list to fill the Login html.TextBoxFor automatically with his details.
Currently the Login textbox remains empty when i choose a person from dropdown list.
So i ve got my json object and tested as well my sql which is fine so i suppose the issue must be somewhere in jquery.
I would be glad if you could help me find the error.
View :
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>User</legend>
<div class="editor-label">#Html.LabelFor(model => model.UserLogin)</div>
<div class="editor-field">#Html.TextBoxFor(model => model.UserLogin, new {id ="LoginId" })
#Html.ValidationMessageFor(model => model.UserLogin)</div>
<div class="editor-label">#Html.LabelFor(model => model.UserFullName)</div>
<div class="editor-field">#Html.DropDownList("UserFullName", ViewBag.UserFullName as SelectList, "Select a User", new { id = "UserID" })
#Html.ValidationMessageFor(model => model.UserFullName)</div>
<p>
<input type="submit"
value="Create" />
</p>
</fieldset> }
<div>#Html.ActionLink("Back to List", "Index")</div>
<script type="text/javascript">
$('#UserID').on('change', function () {
$.ajax({
type: 'POST',
url: '#Url.Action("GetUserForm")',
data: { FullName: $('#UserID').val() },
success: function (results){
var login = $('#LoginId');
login.empty();
$.each(results, function ()
{
login.val(this.ID).text(this.Value);
});
}});
});
</script>
Controller:
public ActionResult Create()
{
var names = StaffDB.StaffData.AsEnumerable().Select(s => new
{
ID = s.ID,
FullName = string.Format("{0} {1}", s.Forename1, s.Surname)
}).ToList();
if(ModelState.IsValid)
{
db.Users.Add(user);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UserFullName = new SelectList(names, "FullName", "FullName", user.UserFullName);
return View(user);
}
[HttpPost]
public JsonResult GetUserForm(string FullName)
{
//pseudo code
var data = from s in StaffDB.StaffData
where s.Forename1 + ' ' + s.Surname == FullName
select new
{
Value = s.Login,
ID = s.ID
};
return Json(data);
}
I think the issue is while returning the json, In MVC by default Jsonresult is "Deny get", so you have add "Allow Get".
[HttpPost]
public JsonResult GetUserForm(string FullName)
{
//pseudo code
var data = from s in StaffDB.StaffData
where s.Forename1 + ' ' + s.Surname == FullName
select new { Value = s.Login, ID = s.ID };
if (data == null)
return Json(null);
return Json(data , JsonRequestBehavior.AllowGet);
}
I was doing tutorial from this page here, I was mostly copying
http://ricardocovo.com/2011/04/03/asp-mvc3-editing-records-with-jqueryui-dialogs-and-ajaxforms/
And it is not working and I don't know why. There is no reaction at all.
This is my Edit Method
public ActionResult Edit(int id)
{
ViewBag.Categories = CategoriesSelectList();
return PartialView(proxy.GetProduct(id));
}
This is my Edit View (Partial View). But I dont see any difference from the normal view.
#model Shop.Data.ProductType
<h2>Edit</h2>
#using (Ajax.BeginForm("Edit", "Shop", null,
new AjaxOptions
{
UpdateTargetId = "update-message",
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
OnSuccess = "updateSuccess"
}, new { id = "updateShopForm" }))
{
#Html.ValidationSummary(true)
<div id="update-message" class="error invisible"></div>
<fieldset>
<legend>Products</legend>
#Html.HiddenFor(model => model.CategoryID)
#Html.Label("Nazwa")
<div class="editor-label">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<br />
<br />
#Html.Label("Kategoria")
<div class="editor-label">
#Html.DropDownList("CategoryID", new SelectList(ViewBag.Categories, "value", "text"))
#Html.ValidationMessageFor(model => model.CategoryID)
</div>
</fieldset>
}
And here is this long java from tutorial
<div id="updateDialog" title="Update Product">
</div>
<script type="text/javascript">
var linkObj;
$(function () {
$(".edit-link").button();
$('#updateDialog').dialog({
autoOpen: false,
width: 400,
resizable: false,
modal: true,
buttons: {
"Update": function () {
$("#update-message").html(''); //make sure there is nothing on the message before we continue
$("#updateShopForm").submit();
},
"Cancel": function () {
$(this).dialog("close");
}
}
});
$(".edit-link").click(function () {
//change the title of the dialgo
linkObj = $(this);
var dialogDiv = $('#updateDialog');
var viewUrl = linkObj.attr('href');
$.get(viewUrl, function (data) {
dialogDiv.html(data);
//validation
var $form = $("#updateShopForm");
// Unbind existing validation
$form.unbind();
$form.data("validator", null);
// Check document for changes
$.validator.unobtrusive.parse(document);
// Re add validation with changes
$form.validate($form.data("unobtrusiveValidation").options);
//open dialog
dialogDiv.dialog('open');
});
return false;
});
});
function updateSuccess() {
if ($("#update-message").html() == "True") {
//we update the table's info
var parent = linkObj.closest("tr");
parent.find(".carName").html($("#Name").val());
parent.find(".carDescription").html($("#Description").val());
//now we can close the dialog
$('#updateDialog').dialog('close');
//twitter type notification
$('#commonMessage').html("Update Complete");
$('#commonMessage').delay(400).slideDown(400).delay(3000).slideUp(400);
}
else {
$("#update-message").show();
}
}
</script>
And here is my edit link
#Html.ActionLink("Edytuj", "Edit", new { id = m.ID }, new { #class = "edit-link" })
Can't find any difference.
I'm using MVC 4 and Entity Framework to develop a web app. I'm working with partial views which are loaded with javascript. One of them is a create view which includes validation. And that's my problem : the validation. I have a custom validation logic and, for example, if a user enters some numbers into a field such as "Name", it displays an error.
Here, with the partial views, it redirects me on my partial with the errors displayed but what I wanted to do is to stay on my main view (Index view) and keep my partial view which displays the errors.
EDIT :
Here is my partial view :
#model BuSIMaterial.Models.Person
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Person</legend>
<div class="editor-label">
First name :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.FirstName, new { maxlength = 50 })
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
Last name :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.LastName, new { maxlength = 50 })
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
National number :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.NumNat, new { maxlength = 11 })
#Html.ValidationMessageFor(model => model.NumNat)
</div>
<div class="editor-label">
Start date :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.StartDate, new {#class = "datepicker", #placeholder="yyyy/mm/dd"})
#Html.ValidationMessageFor(model => model.StartDate)
</div>
<div class="editor-label">
End date :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.EndDate, new { #class = "datepicker", #placeholder = "yyyy/mm/dd" })
#Html.ValidationMessageFor(model => model.EndDate)
</div>
<div class="editor-label">
Distance House - Work (km) :
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HouseToWorkKilometers)
#Html.ValidationMessageFor(model => model.HouseToWorkKilometers)
</div>
<div class="editor-label">
Category :
</div>
<div class="editor-field">
#Html.DropDownList("Id_ProductPackageCategory", "Choose one ...")
#Html.ValidationMessageFor(model => model.Id_ProductPackageCategory) Add a new category?
</div>
<div class="editor-label">
Upgrade? :
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Upgrade)
#Html.ValidationMessageFor(model => model.Upgrade)
</div>
<br />
<div class="form-actions">
<button type="submit" class="btn btn-primary">Create</button>
</div>
</fieldset>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/jqueryui")
#Styles.Render("~/Content/themes/base/css")
}
In my view Index, I have this :
<div class="form-actions"><button type="button" id="create" class="btn btn-primary">Create</button> </div>
<div id ="create_person"></div>
And the way I load my Partial View :
$("#create").click(function () {
var form = $("#create_person").closest("form");
form.removeData('validator');
form.removeData('unobtrusiveValidation');
$.validator.unobtrusive.parse(form);
$.ajax({
url: "/Person/CreateOrUpdate",
type: "POST",
data: $("#create_person").serialize(),
cache: false
});
// var url = '/Person/CreatePerson';
// $("#create_person").load(url);
});
The actions :
[HttpGet]
public ActionResult CreateOrUpdate()
{
ViewBag.Id_ProductPackageCategory = new SelectList(db.ProductPackageCategories, "Id_ProductPackageCategory", "Name");
return View();
}
[HttpPost]
public JsonResult CreateOrUpdate(Person person)
{
ViewBag.Id_ProductPackageCategory = new SelectList(db.ProductPackageCategories, "Id_ProductPackageCategory", "Name", person.Id_ProductPackageCategory);
try
{
if (!ModelState.IsValid)
{
string messages = string.Join("; ", ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
throw new Exception("Please correct the following errors: " + Environment.NewLine + messages);
}
db.Persons.AddObject(person);
db.SaveChanges();
return Json(new { Result = "OK" });
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
If you post the page it will not come back to the dynamically loaded partial view. Try to make a ajax call to /Person/CreatePerson. Your CreatePerson will look similar to
[HttpPost]
public JsonResult CreatePerson(Person person)
{
ViewBag.Id_ProductPackageCategory = new SelectList(db.ProductPackageCategories, "Id_ProductPackageCategory", "Name", person.Id_ProductPackageCategory);
try
{
if (!ModelState.IsValid)
{
string messages = string.Join("; ", ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
throw new Exception("Please correct the following errors: " + Environment.NewLine + messages);
}
db.Persons.AddObject(person);
db.SaveChanges();
return Json(new { Result = "OK" });
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
} `
The ajax call to /Person/CreatePerson will look similar to
`
$.ajax({
url: '/Person/CreatePerson',
type: "POST",
data: $("#form").serialize(),
success: function (responce) {
alert(responce.Message);
},
error: function (xhr, textStatus) {
alert(xhr.status + " " + xhr.statusText);
}
});
Besides unobtrusive validation will not work easily with dynamic content. check the link unobtrusive validation on dynamically added partial view (not working)
I've developed a decent workaround for this. The partial page won't show the server errors on postback. First of all, we get the errors, send them back to the page, then create them in javascript & revalidate the page. Hopefully, hey presto!
In your controller:
if (ModelState.IsValid)
{
//... whatever code you need in here
}
var list = ModelStateHelper.AllErrors(ModelState);
TempData["shepherdErrors"] = list;
I put it in TempData so it can be retrieve easily from the partial. Yes, I called it shepherdErrors, it's my idea so I can call the concept whatever silly name I want! Shepherd the error codes to where they should be or something being the general idea.
In a helper class:
public class ModelStateHelper
{
public static IEnumerable<KeyValuePair<string, string>>
AllErrors(ModelStateDictionary modelState)
{
var result = new List<KeyValuePair<string, string>>();
var erroneousFields = modelState.Where(ms => ms.Value.Errors.Any())
.Select(x => new { x.Key, x.Value.Errors });
foreach (var erroneousField in erroneousFields)
{
var fieldKey = erroneousField.Key;
var fieldErrors = erroneousField.Errors
.Select(error => new KeyValuePair<string, string>(fieldKey, error.ErrorMessage)); //Error(fieldKey, error.ErrorMessage));
result.AddRange(fieldErrors);
}
return result;
}
}
Then on the html page somewhere after jquery being loaded:
function displayShepherdErrors() {
var shepherdErrors = JSON.parse('#(Newtonsoft.Json.JsonConvert.SerializeObject(TempData["shepherdErrors"]))'.replace(/"/g, '"'));
var frm;
var isShepherdErrors = (shepherdErrors && shepherdErrors.length > 0);
if (isShepherdErrors) {
errObj = {};
for (var i = 0; i < shepherdErrors.length; i++) {
var errorKey = shepherdErrors[i].Key; //also the id of the field
var errorMsg = shepherdErrors[i].Value;
var reg = new RegExp('^' + errorKey + '$', 'gi');
//find the selector - we use filter so we can find it case insensitive
var control = $('input').filter(function () {
if ($(this).attr('id'))
return $(this).attr('id').match(reg);
});
if (control && control.length) {
control = control[0];
var controlId = $(control).attr('name');
errObj[controlId] = errorMsg;
//get the containing form of the first input element
if (!frm)
frm = control.form;
}
}
var validator = $(frm).validate();
validator.showErrors(errObj);
}
return isShepherdErrors;
}
var isShepherdErrors = displayShepherdErrors();
This should work out of the box with general MVC development provided text boxes that are generated are based on the variable names - this is default behaviour of MVC.
I have a partial view on a cshtml page as follows :-
#model MvcCommons.ViewModels.CompositeViewModel
#{
ViewBag.Title = "Edit";
}
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Article</legend>
#Html.HiddenFor(model => model.ArticleViewModel.Article.ArticleID)
<div class="editor-label">
#Html.LabelFor(model => model.ArticleViewModel.Article.CategoryID, "Category")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.ArticleViewModel.Article.CategoryID, (SelectList)ViewBag.CategoryID)
#Html.ValidationMessageFor(model => model.ArticleViewModel.Article.CategoryID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ArticleViewModel.Article.ArticleTitle)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ArticleViewModel.Article.ArticleTitle)
#Html.ValidationMessageFor(model => model.ArticleViewModel.Article.ArticleTitle)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ArticleViewModel.Article.ArticleDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ArticleViewModel.Article.ArticleDate)
#Html.ValidationMessageFor(model => model.ArticleViewModel.Article.ArticleDate)
</div>
#Html.HiddenFor(model => model.PageViewModel.Page.PageTitle, new { id = "PageTitle" })
#Html.HiddenFor(model => model.PageViewModel.Page.PageAction, new { id = "PageAction" })
#Html.HiddenFor(model => model.ArticleViewModel.Article.ArticleID, new { id = "ArticleID" })
<div class="ImageGallery">
#Html.Partial("~/Views/Shared/ImageGallery.cshtml", Model)
</div>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
The ImageGallery.cshtml Partial View is as follows :-
#model MvcCommons.ViewModels.CompositeViewModel
#{
ViewBag.Title = "Modal image uploader";
}
<script type="text/javascript">
var pageTitle = $('#PageTitle').val();
var pageAction = $('#PageAction').val();
var id = $('#ArticleID').val();
$(document).ready(function () {
$('.modal_block').click(function (e) {
$('#tn_select').empty();
$('.modal_part').hide();
});
$('#modal_link').click(function (e) {
$('.modal_part').show();
var context = $('#tn_select').load('/Upload/UploadImage?Page=' + pageTitle + '&Action=' + pageAction + '&id=' + id, function () {
initSelect(context);
});
e.preventDefault();
return false;
});
$('#delete_images').click(function (e) {
var sList = "";
$('input[type=checkbox]').each(function () {
var sThisVal = (this.checked ? this.value : "");
sList += (sList == "" ? sThisVal : "," + sThisVal);
});
$.ajax({
url: "/Upload/DeleteImages?IDs=" + sList + '&Page=' + pageTitle + '&Action=' + pageAction + '&id=' + id,
data: sList,
cache: false,
type: "POST",
dataType: "json"
});
reloadGallery();
return false;
});
function reloadGallery() {
$.ajax({
type: "GET",
url: '/Upload/Index/',
data: "{}",
cache: false,
dataType: "html",
success: function (data)
{ $().html(data); }
})
}
});
</script>
<div class="modal_block modal_part"></div>
<div class="modal_dialog modal_part" id="tn_select"></div>
<h2>List of images</h2>
<p>
This page contains the list of all uploaded images.
</p>
#if (Model.ImageViewModel.Images.Count > 0)
{
<div class="imageContainer">
<div class="div-table">
<div class="div-table-row-title">
<div class="div-table-col">Image</div>
<div class="div-table-col">Image Name</div>
</div>
</div>
}
</div>
<div class="DeleteImages">
Delete Selected Images.
</div>
}
else
{
<div class="imageCenter">
No images have been uploaded so far.
</div>
}
<p>
Click here to open modal dialog.
</p>
<div class="clear"></div>
Here is the code in the Controller to delete the images:-
[HttpPost]
public ActionResult DeleteImages(string IDs)
{
_Page = Request.QueryString["Page"];
_Action = Request.QueryString["Action"];
_ItemID = Convert.ToInt32(Request.QueryString["id"]);
Generics.PageIDS currentPage = (Generics.PageIDS)Enum.Parse(typeof(Generics.PageIDS), _Page);
_PageID = Convert.ToInt32(currentPage);
string[] sepImageIds = IDs.Split(',');
foreach (string strImageId in sepImageIds)
{
imageViewModel.DeleteFromXML(strImageId);
}
return RedirectToAction(_Action, _Page, new { id = _ItemID });
}
Everything works fine in this Partial View, except for when I delete an image, the deletion is done correctly, however when the code is passed back to the View, the Partial View is not refreshed.
Is there something I am missing?
Thanks for your help and time!
------------------UPDATE---------------------------------------------
This is the Edit Controller Action after the Delete has finished :-
public ActionResult Edit(int id = 0)
{
articleViewModel.Article = unitOfWork.ArticleRepository.GetByID(id);
pageViewModel.Page.PageTitle = "Article";
pageViewModel.Page.PageAction = "Edit";
if (articleViewModel.Article == null)
{
return HttpNotFound();
}
PopulateDropDownList(articleViewModel.Article.CategoryID);
viewModel.ArticleViewModel = articleViewModel;
int ImageCount = 0;
imageViewModel.Images = imageViewModel.PopulateFromXML(pageViewModel.GetPageID(_PageName), id, out ImageCount).ToList();
viewModel.ImageViewModel = imageViewModel;
viewModel.PageViewModel = pageViewModel;
return View(viewModel);
//return Json(viewModel, JsonRequestBehavior.AllowGet);
}
I think it is because all partial views are cached by default, what I would do is to create a method in the controller to return an ActionResult like so, with the output cache attribute of 0, so it does not cache
[OutputCache(Duration = 0)]
public ActionResult ImageGalleryAction()
{
// do return your cshtml name here
return PartialView("ImageGallery");
}
I would give an id to your imageGalleryDiv, change your reloadGallery method to load the partial view on onload event as well and remove the #Html.Partial like so
<script>
function reloadGallery(){
$('#myImageGallery').load("ImageGalleryAction");
}
</script>
<div id="myImageGallery" class="ImageGallery" onload="reloadGallery()">
</div>
That way, your partial view will be manually injected/refreshed via jquery and it won't be cached.
Cheers
You need to call the reloadGallery function once your DELETE ajax call succeeds, which is inside the success callback:
$.ajax({
url: '/Upload/DeleteImages?IDs=' + sList + '&Page=' + pageTitle + '&Action=' + pageAction + '&id=' + id,
data: sList,
cache: false,
type: 'POST',
success: function(data) {
reloadGallery();
}
});
Not to mention that your DeleteImages action doesn't return any JSON, so you should remove the dataType: 'json' switch. You seem to be redirecting at the end of the DeleteImages action. Are you sure that you are redirecting to a controller action that returns a partial?
Also instead of firing yet another AJAX request in your reloadGallery method why not simply have your DeleteImages action return the partial and inside the success call update the corresponding section of your DOM using the .html method?
I think it might be in your ajax call
function reloadGallery() {
$.ajax({
type: "GET",
url: '/Upload/Index/',
data: "{}",
cache: false,
dataType: "html",
success: function (data)
{ $().html(data); }
})
}
remove the data: "{}" or replace it with data: {}