jquery .valid() = true before remote validation - javascript

I am working in MVC5 and using a combination of client and server validation. For this particular issue I am using remote validation.
The Razorview:
<div id="studentStatus" name="studentStatus">
#using (Html.BeginForm("StudentStatus", "StudentStatus", FormMethod.Post, new { id = "studentStatusForm", data_timer = 240000 }))
{
#Html.HiddenFor(x => x.CurrentStudentSepId, new { #class = "hideMe" })
<div class="row">
<h2 class="col-md-12 topSectionHeader">
#StaffPortalResources.Section_StudentStatus
<span class="savedStatus"></span>
</h2>
</div>
<span class="errorNotification"></span>
<div class="questionGroup form-group">
<div class="row margin-btm-sm">
<div class="col-md-4">
#Html.LabelFor(x => x.StudentStatusId)
#if (Model.IsReadOnly)
{
<div>#Html.DisplayFor(x => x.StudentStatusId)</div>
#Html.HiddenFor(x => x.StudentStatusId)
}
else
{
#Html.CustomComboBoxFor(x => x.StudentStatusId, (new { #class = "statusChangeValidationHandler", data_action = "GetStudentEditableStatuses", data_controller = "Lookup" }))
}
#Html.ValidationMessageFor(x => x.StudentStatusId)
</div>
}
The model:
public sealed class StudentStatusSectionViewModel : SectionViewModel
{
[Display(Name = "Status", ResourceType = typeof(StaffPortalResources))]
[SelectMustExist(true, ErrorMessageResourceType = typeof (StaffPortalResources), ErrorMessageResourceName = "StudentStatus_InvalidSelection")]
[Remote("ValidateStudentStatus", "StudentStatus", AdditionalFields = "CurrentStudentSepId")]
public string StudentStatusId { get; set; }
public bool DoNotReenroll { get; set; }
}
The controller:
public JsonResult ValidateStudentStatus(StudentStatusSectionViewModel model)
{
var errors = _studentStatusSectionAdapter.ValidateStudentStatus(model.CurrentStudentSepId, model.StudentStatusId);
if (errors != null && errors.Any())
{
var currentStatus = _studentStatusSectionAdapter.Get(model.CurrentStudentSepId).StudentStatusId;
Response.AddHeader("status", currentStatus);
return Json(string.Join("</br>", errors), JsonRequestBehavior.AllowGet);
}
return Json(true, JsonRequestBehavior.AllowGet);
}
The remote validation .complete() function:
scope.initializeStudentStatusDropdown = function() {
var $element = $('#studentStatusForm').find('input.statusChangeValidationHandler[data-role="combobox"]');
$element.rules().remote.complete = function (xhr) {
if (xhr.responseText !== 'true')
window.Registration.StudentStatus.fixStudentStatusDropdown($element, xhr.getResponseHeader);
}
}
scope.fixStudentStatusDropdown = function($element,responseHeader) {
$element.kVal(responseHeader('status'));
}
The jquery used to wire validation on change event:
scope.initializeAutoSave = function (form, callBack) {
var $form = $(form);
$form.on("change", "input:not(.ignoreAutoSave)", queueForm);
$form.on("change", "textarea:not(.ignoreAutoSave)", queueForm);
window.Validation.initializeValidator($form);
callBacks[$form.attr('id')] = callBack || function() {};
}
The queueForm method:
function queueForm(e) {
if ($(e.target).valid()) {
var $form = $(e.target).closest("form");
var timer = $form.data("timer");
autoSaveQueue[$form.attr("id")] = { $form: $form, timer: timer, attempt: 0 };
}
}
When tracing through the code after changing the value in the dropdown, the change event fires and calls the queueForm function. the valid() call evaluates to true before the remote validation happens.
It is not until after I step over 'if ($(e.target).valid())' line, and it evaluates to true and steps into the if block. Then it jumps into the remote ValidateStudentStatus function in my controller.
Have I done something wrong here? Am I misunderstanding how the remote validation works?
Any insight or help would be greatly appreciated!

Related

How do I use ACE for TextAreaFor in ASP.NET MVC5 application?

I am trying to Get ACE editor for javaScript purpose but i am not able to post my Editor data when i am clicking Save button its show NULL value in DB.
but when i am using normal Textarea without ACE my data is saving properly.
This is My CSHTML code.
<div class="form-group row">
<div class="col-lg-11" id="JavaScriptEditor">
#Html.Label("JavaScript", new { #class = "control-label" })
#Html.TextAreaFor(x=>x.JavaScript,new {#class="JavaScript"})
#Html.ValidationMessageFor(model => model.JavaScript, "", new { #class = "text-danger" })
</div>
</div>
This is my JS Code:-
<script>
var editor = ace.edit("JavaScript");
editor.setTheme("ace/theme/terminal");
editor.getSession().setMode("ace/mode/javascript");
window.onload = function () {
$("form").submit(function () {
$(".JavaScript").val().editor.getSession().getValue();
})
};
</script>
This is My Controller Code:-
[HttpPost]
[ValidateInput(false)]
public ActionResult AddEdit(Article article, FormCollection formCollection)
{
article.CompanyId = OperatingUser.CompanyId;
if(string.IsNullOrWhiteSpace(article.Slug))
article.Slug = article.Name.GenerateSlug();
if (string.IsNullOrWhiteSpace(article.SlugKey))
article.SlugKey = SlugKey();
//bool isUnique = _articleService.IsSlugUnique(slug: article.Slug, articleId: article.Id);
//if (!isUnique)
// article.Slug = $"{article.Slug}-1";
article.CreatedById = OperatingUser.Id;
article.ModifiedById = OperatingUser.Id;
if (article.Tags != null && article.Tags.Any())
article.Tag = string.Join(",", article.Tags);
if (formCollection.GetValue("SubmitButton").AttemptedValue
.Equals("Save and Publish", StringComparison.OrdinalIgnoreCase))
article.IsPublished = true;
article.RoleIds = formCollection.GetValue("selectedRoles").AttemptedValue;
if (article.Id > 0)
{
article.ModifiedById = OperatingUser.Id;
_articleService.Update(article);
}
else
{
article = _articleService.Add(article);
}
return Json(new { RedirectUrl = Url.Action("Edit", "Content", new { area="cms", id = article.Id }) });
}
I am trying to Save This Property data in Db but i am not able to Save this Please Help me.

Passing model to javascript and actions questions

I’m new to MVC and I’m having a lot of trouble with Partial Views. Hopefully someone can give me some guidance.
We’re trying to port our ASP.Net webforms app to MVC. I’ve created a little test app to try to mimic what we’re doing on many of our pages. Based on sample code I found here, I have a View and a Partial view and a Model that contains the Partial model. The view has a drop down list that hides/shows the partial view.
When the user selects Hidden, I have a javascript method that hides the partial view. If they select Visible, the javascript makes a call to an action where I want to be able to modify the model and send it back to the view. I will also need to be able to hide or show other partial views on the page. In another scenario, I will need to be able to modify TestModel and PartialModel within the javascript function itself and re-render (have the changes reflected to the user).
I currently have two things I can't figure out:
How to access the model in the javascript function
How to pass the model back and forth to the action called from the model
The code, as it stands now, calls the action in which I create a new PartialModel and pass it back to the view. That works but when I click on the save button, the "old" PartialModel is passed to the HTTPPost action. However, I need to modify the existing model and not create a new one.
I've posted the code below. Any help would be greatly appreciated.
Controller
namespace WebApplication1.Controllers
{
public class TestController : Controller
{
// GET: Test
public ActionResult Index()
{
TestModel tm = new TestModel();
tm.Vis = Visibility.Visible;
return View(tm);
}
[HttpPost]
public ActionResult Index(TestModel tm)
{
return View(tm);
}
public ActionResult DDLChangedAction(int id)
{
// I need access to TestModel including TestModel.PartialModel
var pvm = new PartialModel();
pvm.PartialInt = 100;
pvm.PartialString = id.ToString(); ;
pvm.PartialString2 = "asdf";
////return PartialView("_PartialTest", pvm,
//// new ViewDataDictionary(System.Web.Mvc.Html.HtmlHelper.ViewData)
//// {
//// TemplateInfo = new TemplateInfo { HtmlFieldPrefix = HtmlHelper.NameFor(m => m.Partial).ToString() }
//// });
return PartialView("_PartialTest", pvm);
}
private ActionResult PartialView(string v, PartialModel pvm, ViewDataDictionary viewDataDictionary)
{
throw new NotImplementedException();
}
}
}
Model
namespace WebApplication1.Models
{
public class TestModel
{
public TestModel()
{
Partial = new PartialModel();
Partial.PartialInt = 42;
Partial.PartialString = "partialString text";
Partial.PartialString2 = "partialString2 text";
}
public int SelectedItem { get; set; }
public Visibility Vis { get; set; }
public PartialModel Partial { get; set; }
}
public class PartialModel
{
public int PartialInt { get; set; }
public string PartialString { get; set; }
public string PartialString2 { get; set; }
}
public enum Visibility
{
Visible = 1,
Hidden
}
}
View
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>TestModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.Label("Visibility", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("Vis", new SelectList(Enum.GetValues(typeof(Visibility))),
"Select",
new {
#id = "ddlVis",
#class = "form-control"
//onchange = #"ddlChanged();"
})
</div>
<br />
#Html.LabelFor(model => model.SelectedItem, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.SelectedItem, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SelectedItem, "", new { #class = "text-danger" })
</div>
</div>
<div id="partialPlaceHolder">
#Html.Partial("_PartialTest", Model.Partial,
new ViewDataDictionary(Html.ViewData)
{
TemplateInfo = new TemplateInfo { HtmlFieldPrefix = Html.NameFor(m => m.Partial).ToString() }
})
</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>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$('#ddlVis').change(function () {
/* Get the selected value of dropdownlist */
var selectedID = $(this).val();
if (selectedID == 1) {
// access and modify the model and partial model
}
if ($('#ddlVis').val() == "Visible") {
// I can pass an integer here, but how do I pass in the model to the action?
$.get('/Test/DDLChangedAction/' + 123, function (data) {
/* data is the pure html returned from action method, load it to your page */
$('#partialPlaceHolder').html(data);
$('#partialPlaceHolder').show();
});
} else if ($('#ddlVis').val() == "Hidden") {
$('#partialPlaceHolder').hide();
}
});
function ddlChanged() {
alert("projectPtIndChanged")
if ($('#ddlVis').val() == "Hidden") {
alert("in hide");
//debugger;
$("#divHideTest").hide();
// now let's show the partial
var newString = "Ickle";
$.get('/Test/DDLChangedAction' + newString, function (data) {
$('#partialPlaceHolder').html(data);
});
} else {
alert("in show");
$("#divHideTest").show();
}
}
</script>
}
Partial View
#model WebApplication1.Models.PartialModel
<div>
<fieldset>
<legend>PartialTest</legend>
<dl class="dl-horizontal">
<dt>#Html.DisplayNameFor(model => model.PartialInt)</dt>
<dd>#Html.EditorFor(model => model.PartialInt)</dd>
<dt>#Html.DisplayNameFor(model => model.PartialString)</dt>
<dd>#Html.EditorFor(model => model.PartialString)</dd>
<dt>#Html.DisplayNameFor(model => model.PartialString2)</dt>
<dd>#Html.EditorFor(model => model.PartialString2)</dd>
</dl>
</fieldset>
</div>

Repopulate Dropdown List After Ajax Request [duplicate]

This is how i am loading on page load state and city dropdown:
My Controller method:
This is the first method which is calling when page is loaded.
public ActionResult Index()
{
var states = GetStates();
var cities = Enumerable.Empty<SelectListItem>();
ViewBag.States = states;
ViewBag.Cities = cities;
}
private IEnumerable<SelectListItem> GetStates()
{
using (var db = new DataEntities())
{
return db.States.Select(d => new SelectListItem { Text = d.StateName, Value =d.Id.ToString() });
}
}
[HttpGet]
public ActionResult GetCities(int id)
{
using (var db = new DataEntities())
{
var data = db.Cities.Where(d=>d.StateId==id).Select(d => new { Text = d.CityName, Value = d.Id }).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
}
My View:
IEnumerable<SelectListItem> States = ViewBag.States;
IEnumerable<SelectListItem> Cities = ViewBag.Cities;
#Html.DropDownList("State", States, "Select State", new { onchange="loadCities(this)"})
#Html.DropDownListFor(m => m.CityId, Cities, "Select City", new { id="ddlCity"})
function loadCities(obj) {
$.ajax({
url: "/Home/GetCities",
data: { id: $(obj).val() },
contentType:"application/json",
success:function(responce){
var html = '<option value="0">Select City</option>';
$(responce).each(function () {
html += '<option value="'+this.Value+'">'+this.Text+'</option>'
});
$("#ddlCity").html(html);
}
});
}
Any better way then this to load state and city dropdown?
public class HomeController : Controller
{
public ActionResult Index(int id=0)
{
Person model = null;
var states = GetStates().ToList();
var cities = Enumerable.Empty<SelectListItem>();
if (id > 0)
{
using (var db = new DataEntities())
{
model = db.People.Include("City").FirstOrDefault(d => d.Id == id);
if (model == null)
model = new Person();
else
{
states.First(d => d.Value == model.City.StateId.ToString()).Selected = true;
cities = db.Cities.Where(d => d.StateId == model.City.StateId).ToList().Select(d => new SelectListItem { Text = d.CityName,Value=d.Id.ToString(),Selected=d.Id==model.CityId });
}
}
}
else
{
model = new Person();
}
ViewBag.States = states;
ViewBag.Cities = cities;
ViewBag.Persons = GetPersons();
return View(model);
}
[HttpGet]
public ActionResult GetCities(int id)
{
using (var db = new DataEntities())
{
var data = db.Cities.Where(d=>d.StateId==id).Select(d => new { Text = d.CityName, Value = d.Id }).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
}
public ActionResult SavePersonDetail([Bind(Exclude = "Id")] Person model)
{
// var employeeDal= new Emploee();
//employee.firstname=model.
if (ModelState.IsValid)
{
var Id = model.Id;
int.TryParse(Request["Id"], out Id);
using (var db = new DataEntities())
{
if (Id > 0)
{
var person = db.People.FirstOrDefault(d => d.Id == Id);
if (person != null)
{
model.Id = Id;
db.People.ApplyCurrentValues(model);
}
}
else
{
db.People.AddObject(model);
}
db.SaveChanges();
}
}
if (!Request.IsAjaxRequest())
{
ViewBag.States = GetStates();
ViewBag.Persons = GetPersons();
ViewBag.Cities = Enumerable.Empty<SelectListItem>();
return View("Index");
}
else
{
return PartialView("_personDetail",GetPersons());
}
}
public ActionResult Delete(int id)
{
using (var db = new DataEntities())
{
var model = db.People.FirstOrDefault(d => d.Id == id);
if (model != null)
{
db.People.DeleteObject(model);
db.SaveChanges();
}
}
if (Request.IsAjaxRequest())
{
return Content(id.ToString());
}
else
{
ViewBag.States = GetStates();
ViewBag.Persons = GetPersons();
ViewBag.Cities = Enumerable.Empty<SelectListItem>();
return View("Index");
}
}
private IEnumerable<SelectListItem> GetStates()
{
using (var db = new DataEntities())
{
return db.States.ToList().Select(d => new SelectListItem { Text = d.StateName, Value =d.Id.ToString() });
}
}
private IEnumerable<Person> GetPersons()
{
using (var db = new DataEntities())
{
return db.People.Include("City").Include("City.State").ToList();
}
}
public ActionResult HomeAjax()
{
ViewBag.States = GetStates();
ViewBag.Cities = Enumerable.Empty<SelectListItem>();
using (var db = new DataEntities())
{
var data = db.States.Include("Cities").Select(d => new { Id = d.Id, Name = d.StateName, Cities = d.Cities.Select(x => new { Id=x.Id,Name=x.CityName}) }).ToList();
ViewBag.CityStateJson = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(data);
}
ViewBag.Persons = GetPersons();
return View();
}
}
#model IEnumerable<Person>
<div>
<table>
<tr>
<th>
First Name
</th>
<th>
Last Name
</th>
<th>
Email
</th>
<th>
City
</th>
<th>
State
</th>
<th>
Edit
</th>
</tr>
#if (Model.Count() == 0)
{
<tr>
<td colspan="6">
<h3>No data available</h3>
</td>
</tr>
}
else {
foreach (var item in Model) {
<tr data-id="#item.Id">
<td data-id="fn">#item.FirstName</td>
<td data-id="ln">#item.LastName</td>
<td data-id="email">#item.Email</td>
<td data-id="cn">#item.CityName<input type="hidden" value="#item.CityId" /></td>
<td>#item.StateName</td>
<td>
#if (ViewBag.Title == "Home Ajax" || Request.IsAjaxRequest())
{
Update
<span>#Ajax.ActionLink("Delete", "Delete", new { id = item.Id }, new AjaxOptions {OnSuccess="deleteSuccess",OnBegin="showLoader",OnComplete="hideLoader" })</span>
}
else {
<span>#Html.ActionLink("Update", "Index", new { id = item.Id })</span>
<span>#Html.ActionLink("Delete", "Delete", new { id = item.Id })</span>
}
</td>
</tr>
}
}
</table>
</div>
#model Person
#{
ViewBag.Title = "Home Ajax";
IEnumerable<Person> persons = ViewBag.Persons;
IEnumerable<SelectListItem> States = ViewBag.States;
IEnumerable<SelectListItem> Cities = ViewBag.Cities;
IEnumerable<State> fullStates=ViewBag.CityStates;
}
#section featured {
<section class="featured">
<div class="content-wrapper">
<hgroup class="title">
<h1>#ViewBag.Title.</h1>
</hgroup>
</div>
</section>
}
#section styles{
<style type="text/css">
td,th {
border:1px solid;
padding:5px 10px;
}
select {
padding:5px 2px;
width:310px;
font-size:16px;
}
</style>
}
#section scripts{
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
var jsonArray = #Html.Raw(ViewBag.CityStateJson)
function clearValues() {
$("input[type='text'],select").val('');
$("input[type='hidden'][name='Id']").val(0);
}
function loadCities(obj) {
for (var i = 0; i < jsonArray.length; i++) {
if (jsonArray[i].Id == parseInt($(obj).val())) {
fillCity(jsonArray[i].Cities);
break;
}
}
}
function Edit(obj, Id) {
// alert("hi")
$("input[type='hidden'][name='Id']").val(Id);
var tr = $(obj).closest("tr");
$("#txtfirstName").val($("td[data-id='fn']", tr).text().trim());
$("#txtlastName").val($("td[data-id='ln']", tr).text().trim());
$("#txtemail").val($("td[data-id='email']", tr).text().trim());
var city = $("td[data-id='cn'] input[type='hidden']", tr).val();
var state;
for (var i = 0; i < jsonArray.length; i++) {
for (var j = 0; j < jsonArray[i].Cities.length; j++) {
if (jsonArray[i].Cities[j].Id == parseInt(city)) {
state = jsonArray[i].Id;
break;
}
}
if (state) {
fillCity(jsonArray[i].Cities);
break;
}
}
$("#ddlState").val(state);
$("#ddlCity").val(city);
}
function fillCity(obj) {
var html = '<option value="0">Select City</option>';
$(obj).each(function () {
html += '<option value="' + this.Id + '">' + this.Name + '</option>'
});
$("#ddlCity").html(html);
}
function deleteSuccess(responce) {
alert("record deleted successfully");
$("tr[data-id='" + responce + "']").remove();
}
function insertSuccess() {
alert("Record saved successfully");
clearValues();
}
function showLoader() {
$("#overlay").show();
}
function hideLoader() {
$("#overlay").hide();
}
</script>
}
<h3>Add Personal Detail</h3>
#using (Ajax.BeginForm("SavePersonDetail", "Home", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "personList" ,OnSuccess="insertSuccess",OnBegin="showLoader",OnComplete="hideLoader"}))
{
#Html.HiddenFor(m => m.Id);
<ol class="round">
<li>
#Html.LabelFor(m => m.FirstName)
#Html.TextBoxFor(m => m.FirstName, new { id = "txtfirstName" })
#Html.ValidationMessageFor(m => m.FirstName)
</li>
<li>
#Html.LabelFor(m => m.LastName)
#Html.TextBoxFor(m => m.LastName, new { id = "txtlastName" })
#Html.ValidationMessageFor(m => m.LastName)
</li>
<li>
#Html.LabelFor(m => m.Email)
#Html.TextBoxFor(m => m.Email, new { id = "txtemail" })
#Html.ValidationMessageFor(m => m.Email)
</li>
<li>
#Html.Label("State")
#Html.DropDownList("State", States, "Select State", new { onchange = "loadCities(this)", id = "ddlState" })
</li>
<li>
#Html.LabelFor(m => m.CityId)
#Html.DropDownListFor(m => m.CityId, Cities, "Select City", new { id = "ddlCity" })
#Html.ValidationMessageFor(m => m.CityId)
</li>
</ol>
<input type="submit" value="Save" />
<input type="button" value="Cancel" onclick="clearValues();"/>
}
<h2>
Person List
</h2>
<div style="position:fixed;text-align:center;top:0;bottom:0;left:0;right:0;z-index:10;background-color:black;opacity:0.6;display:none;" id="overlay">
<img style="position:relative;top:370px" src="~/Images/ajax-loader.gif" />
</div>
<div id="personList">
#Html.Partial("_personDetail", persons)
</div>
You approach using ajax is fine although I would recommend a few better practices including using a view model with properties for StateID, CityID StateList and CityList, and using Unobtrusive JavaScript rather than polluting you markup with behavior, and generating the first ("please select") option with a null value rather than 0 so it can be used with the [Required] attribute
HTML
#Html.DropDownList(m => m.StateID, States, "Select State") // remove the onchange
#Html.DropDownListFor(m => m.CityID, Cities, "Select City") // why change the default ID?
SCRIPT
var url = '#Url.Action("GetCities", "Home")'; // use the helper (dont hard code)
var cities = $('#CityID'); // cache the element
$('#StateID').change(function() {
$.getJSON(url, { id: $(this).val() }, function(response) {
// clear and add default (null) option
cities.empty().append($('<option></option>').val('').text('Please select'));
$.each(response, function(index, item) {
cities.append($('<option></option>').val(item.Value).text(item.Text));
});
});
});
If you were rendering multiple items (say you were asking the user to select their last 10 cities they visited), you can cache the result of the first call to avoid repeated calls where their selections may include cities from the same state.
var cache = {};
$('#StateID').change(function() {
var selectedState = $(this).val();
if (cache[selectedState]) {
// render the options from the cache
} else {
$.getJSON(url, { id: selectedState }, function(response) {
// add to cache
cache[selectedState] = response;
.....
});
}
});
Finally, in response to your comments regarding doing it without ajax, you can pass all the cities to the view and assign them to a javascript array. I would only recommend this if you have a few countries, each with a few cities. Its a matter of balancing the slight extra initial load time vs the slight delay in making the ajax call.
In the controller
model.CityList = db.Cities.Select(d => new { City = d.CountryID, Text = d.CityName, Value = d.Id }).ToList();
In the view (script)
// assign all cities to javascript array
var allCities= JSON.parse('#Html.Raw(Json.Encode(Model.CityList))');
$('#StateID').change(function() {
var selectedState = $(this).val();
var cities = $.grep(allCities, function(item, index) {
return item.CountryID == selectedState;
});
// build options based on value of cities
});
This is a correct approach, but you can simplify your javascript:
function loadCities(obj) {
$.getJSON("/Home/GetCities", function (data) {
var html = '<option value="0">Select City</option>';
$(data).each(function () {
html += '<option value="'+this.Value+'">'+this.Text+'</option>'
});
$("#ddlCity").html(html);
});
}
Further possible simplification:
Add the default item (Select City) server-side, so your javascript will be smaller.
Here's how I'd do it without the page refresh, assuming the list of cities isn't too long.
I'm assuming you can create a GetStatesAndCities method to return a Dictionary.
public ActionResult Index()
{
Dictionary<string, List<String>> statesAndCities = GetStatesAndCities();
ViewBag.StatesAndCities = Json(statesAndCities);
}
Then in the view:
var states = JSON.parse(#ViewBag.StatesAndCities);
function loadCities(obj) {
var cities = states[$(obj).val()];
var html = '<option value="0">Select City</option>';
$(cities).each(function () {
html += '<option value="'+this.Value+'">'+this.Text+'</option>'
});
$("#ddlCity").html(html);
}
This way when the state is changed the cities field with update immediately with no need for callback.
disclaimer: This is not a code answer, there are plenty other answers.
I think best way to keep yourself happy to seperate UI pages from data => turn them into API calls:
/GetCities
/GetStates
Now you can simply leave the select's empty on Razor rendering the page. And use a Jquery/Bootstrap plugin to create an AJAX select box.
This way when the user stops typing his search, this search string can than be send with the AJAX call (eg: /GetStates?search=test) and then a small result set can be send back to the website.
This gives:
Better separation in serveside code
Better User eXperience.
Smaller page loads (since you no longer send all the options to user when he requests the page, only when he opens the select box).
How about using Knockout?
Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model
You have to use ajax for your cities. But with knockout you dont need to write
var html = '<option value="0">Select City</option>';
$(responce).each(function () {
html += '<option value="'+this.Value+'">'+this.Text+'</option>'});
$("#ddlCity").html(html);
in your javascript.Knockout makes it simple.
You can simply write:
function CityModel() {
var self = this; // that means this CityModel
self.cities = ko.observableArray([]);
self.getCities = function () {
$.ajax({
url: "/Home/GetCities",
data: { id: $(obj).val() },
contentType: "application/json",
success: self.cities
});
}
}
ko.applyBindings(new CityModel());
thats all. But you have to bind your data into html elements.
Instead of using :
#Html.DropDownListFor(m => m.CityId, Cities, "Select City", new { id="ddlCity"})
You can use:
<select data-bind="options:cities,optionsValue:"Id",optionsText:"CityName",optionsCaption:"Select City""></select>
or you can mix razor and knockout:
#Html.DropDownListFor(m => m.CityId, Cities, "Select City", new { id="ddlCity",data_bind:"options:cities,optionsValue:\"Id\",optionsText:\"CityName\""})
One more thing you have to call GetCities when State changes, you can :
#Html.DropDownList("State", States, "Select State", new {data_bind:"event:\"change\":\"$root.GetCities\""})
Dont be scare with \"\" things this because " is an escape character and we have to say to razor i want to use " by using \ before it.
You can find more info about knockout :Knockout
And mixing with razor: Razor and Knockout
Ps: yes using knockout is suspend us from Razor and Mvc. You have to write another ViewModel . But like this situations ko is helpful. Mixing razor and knockout is another option for you.

need help to resolve - Uncaught ReferenceError: data is not defined

I know there are many questions/tutorials for this subject, but cannot solve my problem.
I have to ask for your help. Second day cannot find out the solution to this simple problem.
I am trying as in this tutorial - http://www.c-sharpcorner.com/UploadFile/abhikumarvatsa/cascading-dropdownlist-in-Asp-Net-mvc/
That is working fine, but once i try from DB, i am getting error "Uncaught ReferenceError: data is not defined"
Here is my web page
#model testEmpty.Models.Address
#{
ViewBag.Title = "Create";
}
#Scripts.Render("~/bundles/jquery")
<script src="~/Scripts/myScripts/myScripts.js"></script>
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Address</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group col-md-10">
#Html.Label("Zone")
#Html.DropDownList("ZoneId", ViewBag.ZoneName as SelectList, "--Select a Zone--", new { id = "ZoneId" })
#Html.ValidationMessage("Zone", "*")
</div>
<div class="form-group">
<div class="col-md-10">
#Html.Label("Districts of SZ")
<select id="DistrictSZ" name="DistrictSZ"></select>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Controller
private myContext db = new myContext();
// GET: Addresses
public ActionResult Index()
{
var zones = db.Addresses.Include(a => a.Zone);
ViewBag.ZoneName = new SelectList(zones, "Value", "Text");
return View(zones.ToList());
}
public JsonResult DistrictList(int id)
{
var district = from s in db.Districts
where s.ZoneId == id
select s;
return Json(new SelectList(district.ToArray(), "ZoneId", "Name"), JsonRequestBehavior.AllowGet);
}
Script
$(function () {
$('#ZoneId').change(function () {
$.getJSON('DistrictList/' + $('#ZoneId').val(), getDistricts (data));
});
});
function getDistricts(data) {
var items = '<option>Select a District</option>';
$.each(data, function (i, district) {
items += "<option value='" + district.Value + "'>" + district.Text + "</option>";
});
$('#DistrictSZ').html(items);
}
As i understand, my problem is with JSON. what am I doing wrong?
Firstly you do not need to return a SelectList (javascript knows nothing about a c# class)
public JsonResult DistrictList(int id)
{
var district = db.Districts.Where(d => d.ZoneId == id).Select(d => new
{
Value = d.ZoneId, // this look wrong - see note below
Text = d.Name
});
return Json(district, JsonRequestBehavior.AllowGet);
}
Then in your script
var url = '#Url.Action("DistrictList")'; // ensure your url's are properly generated
var districts = $('#DistrictSZ'); // cache it
$('#ZoneId').change(function () {
$.getJSON(url, { id: $(this).val() }, function (data) {
districts.empty(); // remove existing options
districts.append($('</option>').val('').text('Select a District'));
$.each(data, function (i, district) {
districts.append($('</option>').val(district.Value).text(district.Text));
});
});
});
In fact, since ZoneId is always the same, you could just return a collection of the Name values
var district = db.Districts.Where(d => d.ZoneId == id).Select(d => d.Name);
and
$('#ZoneId').change(function () {
var zoneID = $(this).val();
$.getJSON(url, { id: zoneID }, function(data) {
districts.empty(); // remove existing options
districts.append($('</option>').val('').text('Select a District'));
$.each(data, function (i, district) {
districts.append($('</option>').val(zoneID).text(district));
});
});
});
However your code is generating all options with the same value (ZoneId) which does not make much sense, so I suspect you really want to use another property of District - i.e. its Id (or DistrictId?) property.
You're passing the returned value of getDistricts to the callback variable of $.getJSON.
$.getJSON('DistrictList/' + $('#ZoneId').val(), getDistricts (data));
You need to pass the function reference like this
$.getJSON('DistrictList/' + $('#ZoneId').val(), getDistricts);
maybe you should handle your callback function with a anonymous function like this:
$.getJSON('DistrictList/' + $('#ZoneId').val(), success(data){
getDistricts(data);
})

Mvc4 persist order of Jquery sortable list items to controller

I have a list of Items that I want the user to sort using a Jquery sortable.
the structure of the items is like this:
public class SortableItems
{
public int Code { get; set; }
public byte? Priority { get; set; }
public string Name { get; set; }
public int IsActive { get; set; }
}
in my view I render the sortable inside a form in the following way:
#using (Html.BeginForm("TestAction", "TestController", FormMethod.Post, new { id = "submitTest" }))
{
<ul id="sortable" style="padding:0px;">
#for (int i = 0; i < Model.List.Count(); i++)
{
<li class="ui-state-default">
#Html.HiddenFor(x => Model.List[i].Code, new { #class = "id" })
#Html.HiddenFor(x => Model.List[i].Priority, new { #class = "order" })
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
#Model.List[i].Name
</li>
}
</ul>
<input type="submit" class="btn btn-primary" value="Update" />
}
Now the user can sort the N items in the list to their hearts desire. But I have not found a way to update the pidden value for Priority (order of the items that the user has sorted)
How can I persist the order of the items to my controller?
this is how I managed to solve the problem:
on the client side I did the following:
var neworder = new Array();
$("#sortable").disableSelection();
$("#sortable").sortable({
stop: function (event, ui) {
neworder = new Array();
$('#sortable li').each(function () {
//get the id
var id = $(this).attr("data-id");
neworder.push(id);
});
}
});
$(function () {
$('#submitTest').submit(function () {
if (neworder.length < 1) {
return false;
}
$.ajax({
url: "GuardarPrioridad", type: "POST", dataType: "json",
data: { orden: neworder.toString() },
success: function (result) {
$('#ShowResultHere').text(result);
$("#submitTest").hide();// .tex.html(result);
}
});
return false;
});
});
each time the user moves an element I update an array that conatins the IDs of the elements in the order that the user put them in.
I then submit this array via ajax and it is recieved by the controller.
and my controller looks like this:
public JsonResult SavePriority(string orden)
{
string[] data= orden.Split(',');
for (int i = 0; i < droguerias.Length; i++)
{
//save to DB
}
return Json("Saved!");
}

Categories