Cascaded dropdown Validation not working with Data Annotated as Required - asp.net-mvc

ISSUE / Error EXPLANATION :
I have a cascaded dropdownlist in which first user will select a
country and as per his selection cities are getting populated via ajax in which no city is selected initially but just a SELECT option is selected. I have populated my cities at the change event of country dropdown using ajax JSON data from the controller but it seems to me when I call #CityId.Empty() its removing all options from the city including the SELECT option which I re inserted through jquery code but that line is getting treated as an Option Item instead of OPTION SELECT due to which validation is assuming it a user selection where as in reality its a message to the user to select any given city. SO please help me out to do that validation working if no city is selected it should not allow me to submit the form.
Controller CODE :
public ActionResult PostAd(ClassifiedItem classifiedItem)
{
if (ModelState.IsValid)
{
classifiedItem.PostedDate = DateTime.Now;
classifiedItem.Obsolete = false;
classifiedItem.Status = "POSTED";
db.ClassifiedItems.Add(classifiedItem);
db.SaveChanges();
return View("ImageUploader", classifiedItem);
}
else
{
if (classifiedItem.City_Id == 0 || classifiedItem.City_Id == null)
{
ViewBag.Country_Id = new SelectList(db.Countries, "Id", "Country_Name");
ViewBag.City_Id = new SelectList(new List<City>(), "Id", "City_Name",0);
}
else
{
SetCascadedCountry(classifiedItem.City_Id);
}
Prepare_LOV_Data();
}
return View(classifiedItem);
}
View Code :
<div class="form-group">
<label class="control-label col-md-3">Country</label>
<div class="col-md-6">
#Html.DropDownList("Country_Id", null, "--Select Country--", htmlAttributes: new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3">City</label>
<div class="col-md-6">
#Html.DropDownList("City_Id", null,"--Select City--", htmlAttributes: new { #class = "form-control"})
#Html.ValidationMessageFor(model => model.City_Id, "", new { #class = "text-danger" })
</div>
</div>
My Script
$('document').ready(function () {
$('#Country_Id').on('change', function (d) {
$.ajax({
url: 'GetCities',
method: 'Get',
cache: false,
data: { CountryId: $(this).val() },
success: function (d) {
$('#City_Id').empty();
//$('#City_Id').append($('<option/>', { value: 0, text: '--Select City--' }));
$(d).each(function (index, item) {
$('#City_Id').append($('<option/>', { value: item.Id, text: item.City_Name }));
})
},
error: function (d) {
alert(d);
}
})
});
})

Related

ASP.NET MVC return 500 for dropdownlist cascading

Am using it for cascading dropdownlist for country and state. When I click on
country dropdownlist that suppose to display states, I got 500 server error. Please help me
The error occurs when I click the Country dropdownlist
[HttpPost]
[ValidateAntiForgeryToken] //this is to prevent CSRF attack
public ActionResult Create(CITIES ci)
{
List<COUNTRIES> allCountry = new List<COUNTRIES>();
List<STATES> allState = new List<STATES>();
using (AdminEntities dc = new AdminEntities())
{
allCountry = dc.COUNTRIES.OrderBy(a => a.COUNTRY_NAME).ToList();
if (ci != null && ci.COUNTRY_ID > 0)
{
allState = dc.STATES.Where(a => a.COUNTRY_ID.Equals(ci.COUNTRY_ID)).OrderBy(a => a.STATE_NAME).ToList();
}
}
ViewBag.COUNTRY_ID = new SelectList(allCountry, "COUNTRY_ID", "COUNTRY_NAME", ci.COUNTRY_ID);
ViewBag.STATE_ID = new SelectList(allState, "STATE_ID", "STATE_NAME", ci.STATE_ID);
if (ModelState.IsValid)
{
using (AdminEntities dc = new AdminEntities())
{
dc.CITIES.Add(ci);
dc.SaveChanges();
ModelState.Clear();
ci = null;
ViewBag.Message = "Successfully Saved";
}
}
else
{
ViewBag.Message = "Failed! Please try again";
}
return View(ci);
}
[HttpGet]
public JsonResult GetStates(string countryID = "")
{
// List<COUNTRIES> allCountry = new List<COUNTRIES>();
List<STATES> allState = new List<STATES>();
int ID = 0;
if (int.TryParse(countryID, out ID))
{
using (AdminEntities dc = new AdminEntities())
{
allState = dc.STATES.Where(a => a.COUNTRY_ID.Equals(ID)).OrderBy(a => a.STATE_NAME).ToList();
}
}
if (Request.IsAjaxRequest())
{
return new JsonResult
{
Data = allState,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
return new JsonResult
{
Data = "Not valid request",
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
view
#model BPP.CCSP.Admin.Web.Models.CITIES
Create
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="form-horizontal">
<h4>CITIES</h4>
<hr />
#if(ViewBag.message != true)
{
<div style="border:solid 1px black">
#ViewBag.message
</div>
}
<div class="form-group">
#Html.LabelFor(model => model.COUNTRY_ID, "COUNTRY_ID", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#* #Html.DropDownListFor(model=>model.COUNTRY_ID, new SelectList(string.Empty, "Value", "Text"), "Please select a country", new { #style = "width:250px;" }) *#
#Html.DropDownListFor(model => model.COUNTRY_ID, #ViewBag.COUNTRY_ID as SelectList,"Select Country")
#Html.ValidationMessageFor(model => model.COUNTRY_ID)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.STATE_ID, "STATE_ID", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#* #Html.DropDownListFor(model => model.STATE_ID, new SelectList(string.Empty, "Value", "Text"), "Please select a state", new { #style = "width:250px;" }) *#
#Html.DropDownListFor(model => model.STATE_ID, #ViewBag.STATE_ID as SelectList, "Select State")
#Html.ValidationMessageFor(model => model.STATE_ID)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CITY_NAME, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CITY_NAME)
#Html.ValidationMessageFor(model => model.CITY_NAME)
</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")
<script language="javascript">
$(document).ready(function () {
$("#COUNTRY_ID").change(function () {
//this will call when country dropdown select change
var countryID = parseInt($("#COUNTRY_ID").val());
if (!isNaN(countryID))
{
var ddState = $("#STATE_ID");
ddState.empty(); //this line is to clear all items from statee dropdown
ddState.append($("<option></option>").val("").html("Select State"));
//here i will call controller action via jquery to load state for selected country
$.ajax({
url: "#Url.Action("GetStates","CITIES")",
type: "GET",
data: {countryID : countryID},
dataType: "json",
success: function (data)
{
$.each(data, function (i, val) {
ddState.append(
$("<option></option>").val(val.STATE_ID).html(val.STATE_NAME)
);
});
}
,
error: function ()
{
alert("Error!");
}
});
}
});
});
</script>
}
Please what do I do
can you try
var data = {"countryID" : countryID};
$.ajax({
url: '#Url.Content("~/CITIES/GetStates")',
type: "GET",
contentType: 'application/json',
data:JSON.stringify(data),
});
and then debug GetStates action and if you get error there you will get normally 500 server error

Problems with updating modal after transfer to area

I use modal to include new terms in dropdownlist.
<div class="form-group">
#Html.LabelFor(model => model.NatureKindID, "Kind of nature", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div id="divDropNatureKinds">
#Html.Action("ReloadDropDownListNatureKinds", "Modals")
</div>
#Ajax.ActionLink(" ", "CreateModalNatureKinds", "Modals", null, new AjaxOptions { UpdateTargetId = "modaldropbody", OnComplete = "openModal" }, new { #class = "glyphicon glyphicon-plus" })
#Html.ValidationMessageFor(model => model.NatureKindID, "", new { #class = "text-danger" })
</div>
And Also in the main form has a div where modal is building:
<div class="modal fade" id="modaldrop" role="dialog">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div id="modaldropbody" class="modal-body">
</div>
</div>
</div>
And also an script:
#section scripts {
<script>
function openModal() {
$("#modaldrop").modal()
}
</script>
}
When user hit the Action Link the "CreateModalNatureKinds" modal is open. It is a simple form and works well.
New nature types are saved in the database and everything is fine at this point.
So from this form I'll just show the script triggered by the close button that closes the modal and triggers reloading of the dropdownlist div from the main screen
<script>
$("#btnClose").click(function () {
$("#divDropNatureKinds").load("/Modals/ReloadDropDownListNatureKinds");
$("#modal").close;
});
</script>
ReloadDropDownListNatureKinds have only:
#{
Layout = null;
}
#Html.DropDownList("NatureKindID", null, "Select one", htmlAttributes: new { #class = "form-control" })
And Controller ActionResult is:
public ActionResult ReloadDropDownListNatureKinds(int? idNatureKind)
{
if (idNatureKind == null)
ViewBag.NatureKindID = ListToDropDown();
else
{
ViewBag.NatureKindID = ListaToDropDown(idNatureKind);
}
return PartialView();
}
public SelectList ListToDropDown()
{
return ListToDropDown(null);
}
public SelectList ListaToDropDown(int? idNatureKind)
{
var list = db.NatureKinds
.Select(u => new
{
Text = u.NatureName,
Value = u.ID
}).ToList();
SelectList ret = new SelectList(list.OrderBy(x => x.Text), "Value", "Text", idNatureKind);
return ret;
}
And the structure was:
To be clear. ALL THIS WORKS PERFECTLY.
But, my project was getting very big. A mix of over 50 controllers and more than 250 views. So I decided to look for some way to organize and I found a post about Areas.
I created 5 areas and moved models, controllers and views for each of them.
And now the structure is:
I put in the main View the references of the area where the modal is:
<div class="form-group">
#Html.LabelFor(model => model.NatureKindID, "Kind of nature", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div id="divDropNatureKinds">
#Html.Action("ReloadDropDownListNatureKinds", "Modals", new { area = "Admin" })
</div>
#Ajax.ActionLink(" ", "CreateModalNatureKinds", "Modals", new { area = "Admin" }, new AjaxOptions { UpdateTargetId = "modaldropbody", OnComplete = "openModal" }, new { #class = "glyphicon glyphicon-plus" })
#Html.ValidationMessageFor(model => model.NatureKindID, "", new { #class = "text-danger" })
</div>
</div>
With this new structure, most features are working well. When I click ActionLink, the modal opens. In the modal, when registering a new type of nature, it is registered in the database.
When I click the modal close button, the ActionResult ReloadDropDownListNatureKinds is executed and the new list is loaded with the new Nature type that was created.
All of this has been tested.
But the list in the main View is not updated, it does not receive the new list.
It is as if the ViewBag.NatureKindID that receives the new list is not the same as that used in the Main View, it continues with the old list.
Any suggestion? Thanks in advance for any help.

How to pass selected files in Kendo Upload as parameter in ajax request

After much of struggle im posing this question. Im using a Kendo Upload on a page. Am able to post the selected files on the asyn mode with whe the page has Html.BeginForm. But I'm not able to send file details as HttpPostedFileBase when I use ajax request to send data to the controller.
Following is my html
<form class="form-horizontal" role="form">
<div class="form-group">
#Html.Label("Complaint Name", new { #class = "col-sm-3 control-label" })
<div class="col-sm-4">
#Html.TextBoxFor(
m => m.ComplaintName,
new
{
#TabIndex = "1",
#class = "form-control input-sm",
disabled = true
})
</div>
</div>
<div class="form-group">
#Html.Label("Complaint Details", new { #class = "col-sm-3 control-label" })
<div class="col-sm-4">
#Html.TextBoxFor(
m => m.ComplaintDetails,
new
{
#TabIndex = "2",
#class = "form-control input-sm",
disabled = true
})
</div>
</div>
<div class="form-group">
#Html.Label("Choose files to upload", new { #class = "col-sm-3 control-label" })
<div class="col-sm-9 nopaddingforTextArea">
<input name="files" id="files" type="file" />
</div>
</div>
<div class="form-group">
<div>
<input id="btnSubmit" class="btn btn-primary pull-right" type="button" />
</div>
</div>
</form>
Following is my action
public ActionResult SaveComplaintDetails(string complaintName, string complaintDetails, IEnumerable<HttpPostedFileBase> files)
{
}
Following is my js
$("#files").kendoUpload({
async: {
saveUrl: '#Url.Action("EsuperfundCommentsBind", ClientInboxConstants.NewQuery)',
autoUpload: false
},
multiple: true
});
$("#btnSubmit").click(function () {
//Ajax call from the server side
$.ajax({
//The Url action is for the Method FilterTable and the Controller PensionApplicationList
url: '#Url.Action("SaveComplaintDetails", "Complaints")',
//The text from the drop down and the corresponding flag are passed.
//Flag represents the Index of the value in the dropdown
data: {
complaintName: document.getElementById("ComplaintName").value,
complaintDetails: document.getElementById("ComplaintDetails").value,
files: //What to pass here??
},
contentType: "application/json; charset=utf-8",
//Json data
datatype: 'json',
//Specify if the method is GET or POST
type: 'GET',
//Error function gets called if there is an error in the ajax request
error: function () {
},
//Gets called on success of the Ajax Call
success: function (data) {
}
});
});
My question is how to pass the selected files in Kendo Upload in ajax as a parameter?
Any help in this aspect would be really appreciated.
If your view is based on a model and you have generated the controls inside <form> tags, then you can serialize the model to FormData using:
<script>
var formdata = new FormData($('form').get(0));
</script>
This will also include any files generated with: <input type="file" name="myImage" .../> and post it back using:
<script>
$.ajax({
url: '#Url.Action("YourActionName", "YourControllerName")',
type: 'POST',
data: formdata,
processData: false,
contentType: false,
});
</script>
and in your controller:
[HttpPost]
public ActionResult YourActionName(YourModelType model)
{
}
or (if your model does not include a property for HttpPostedFileBase)
[HttpPost]
public ActionResult YourActionName(YourModelType model,
HttpPostedFileBase myImage)
{
}
If you want to add additional information that is not in the form, then you can append it using
<script>
formdata.append('someProperty', 'SomeValue');
</script>
**Example Usage :**
View :
#using (Html.BeginForm("Create", "Issue", FormMethod.Post,
new { id = "frmCreate", enctype = "multipart/form-data" }))
{
#Html.LabelFor(m => m.FileAttachments, new { #class = "editor-label" })
#(Html.Kendo().Upload()
.HtmlAttributes(new { #class = "editor-field" })
.Name("files")
)
}
<script>
$(function () {
$('form').submit(function (event) {
event.preventDefault();
var formdata = new FormData($('#frmCreate').get(0));
$.ajax({
type: "POST",
url: '#Url.Action("Create", "Issue")',
data: formdata,
dataType: "json",
processData: false,
contentType: false,
success: function (response) {
//code omitted for brevity
}
});
});
});
</script>
*Controller :*
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Exclude = null)] Model viewModel, IEnumerable<HttpPostedFileBase> files)
{
//code omitted for brevity
return Json(new { success = false, message = "Max. file size 10MB" }, JsonRequestBehavior.AllowGet);
}
<script>
$(function () {
$('form').submit(function (event) {
event.preventDefault();
var formdata = new FormData($('#createDetail').get(0));
$.ajax(
{
type: 'POST',
url: '#Url.Action("Detail_Create", "Product")',
data: formdata,
processData: false,
success: function (result) {
if (result.success == false) {
$("#divErr").html(result.responseText);
} else {
parent.$('#CreateWindowDetail').data('kendoWindow').close();
}
},
error: function (xhr, ajaxOptions, thrownError) {
$("#divErr").html(xhr.responseText);
}
});
});
});
#using (Html.BeginForm("Detail_Create", "Product", FormMethod.Post, new { id = "createDetail", enctype="multipart/form-data"}))
{
<div id="divErr" class="validation-summary-errors"></div>
<fieldset>
<ol>
<li>
#Html.LabelFor(m => m.Price)
#(Html.Kendo().NumericTextBoxFor(m => m.Price).Name("Price").Format("{0:0}")
.HtmlAttributes(new { style = "width:100%" })
)
</li>
<li>
#Html.LabelFor(m => m.Description)
#Html.TextBoxFor(model => model.Description, new { #class = "k-textbox", id = "Description", style = "width:100%;" })
</li>
<li>
#Html.LabelFor(m => m.Group)
#(Html.Kendo().ComboBox()
.Name("Group")
.Placeholder("Введите группу детали")
.DataTextField("Name")
.DataValueField("Id")
.HtmlAttributes(new { style = "width:100%;" })
.Filter("contains")
.MinLength(1)
.DataSource(source =>
{
source.Read(read =>
{
read.Action("Group_Read_ComboBox", "Product");
})
.ServerFiltering(true);
})
)
</li>
<li>
#Html.LabelFor(m => m.Image)
#(Html.Kendo().Upload()
.Name("files")
)
</li>
</ol>
</fieldset>
<button type="submit" id="get" class="k-button">Добавить</button>
}
[HttpPost]
public ActionResult Detail_Create(DetailModel model, IEnumerable<HttpPostedFileBase> files)
{
string error = string.Empty;
if (ModelState.IsValid)
{
.....
}
IEnumerable<System.Web.Mvc.ModelError> modelerrors = ModelState.SelectMany(r => r.Value.Errors);
foreach (var modelerror in modelerrors)
{
error += "• " + modelerror.ErrorMessage + "<br>";
}
return Json(new { success = false, responseText = error }, JsonRequestBehavior.AllowGet);
}
after pressing the button, the controller null comes to how to fix. 2 days already sitting, and the time comes to an end

bind kendo.data.DataSource to combo using MVVM

Again next question , this time a tricky one,
Datasource:
var dsCountryList =
new kendo.data.DataSource({
transport: {
read: {
dataType: "jsonp",
url: "/Masters/GetCountries"
}
},
schema: {
model: {
id: "CountryID",
fields: {
"CountryDesc": {
}
}
}
}
});
Observable object
function Set_MVVMSupplier() {
vmSupplier = kendo.observable({
SupplierID: 0,
SupplierName: "",
AccountNo: "",
CountryList: dsCountryList,
});
kendo.bind($("#supplierForm"), vmSupplier);
}
here is the html which is bind to observable object , but i am not getting combobox filled, also each time i clicked the combo request goes to server and bring data in json format for countryID, CountryDesc
<div class="span6">
<div class="control-group">
<label class="control-label" for="txtCountryId">Country</label>
<div class="row-fluid controls">
#*<input class="input-large" type="text" id="txtCountryId" placeholder="CountryId" data-bind="value: CountryId">*#
<select id="txtCountryId" data-role="dropdownlist"
data-text-field="CountryDesc" data-value-field="CountryID" , data-skip="true"
data-bind="source: CountryList, value: CountryDesc">
</select>
</div>
</div>
</div>
I didn't get the answer , so i found an alternate working ice of code. just have a look and if it helps then please vote.
created model for ddl in js file
ddl = kendo.data.Model.define({
fields: {
CountryId: { type: "int" },
ConfigurationID: { type: "int" }
}
});
added var ddl to MVVM js file
vmSupplier = kendo.observable({
CountryId: new ddl({ CountryId: 0 }),
ConfigurationID: new ddl({ ConfigurationID: 0 }),});
added code in controller
using (CountriesManager objCountriesManager = new CountriesManager())
{
ViewBag.Countries = new SelectList(
objCountriesManager.GetCountries().Select(p => new { p.CountryID, p.CountryDesc })
, "CountryID", "CountryDesc"); ;
}
added code in cshtml
<div class="span4">
<label class="control-label" for="txtCountryId">Country</label>
#Html.DropDownList("Countries", null,
new System.Collections.Generic.Dictionary<string, object> {
{"id", "txtCountryId" },
{ "data-bind","value: CountryId"} })
</div>
this way i got solved the problem

Enabling the second and third dropdownlistfor based on the value of first dropdownlistfor in MVC

I have three DropDownLists. If a specific Value from my first DropDownList is chosen, the second Dropdownlist should be enabled. Example, if "Player 3" has been choosen, the other two DropDownList should be enabled, though, if "Player 2" is chosen the last DropDownList should be disabled and the second one enabled.
How can I easily do this? I am using MVC 3 EF model first.
This is my code in my view:
<p>Player</p>
<div class="editor-field">
#Html.DropDownListFor(m => m.PlayerName,Model.SubjectTypes, "Choose player" , new { #class = "selectstyle" })
#Html.ValidationMessageFor(model => model.PlayerName)
</div>
<p>Position</p>
<div class="editor-field">
#Html.DropDownListFor(model => model.PositionName, Model.Consultants, "Choose Position", new { #class = "selectstyle" })
#Html.ValidationMessageFor(model => model.ContactPerson)
</div>
<p>Team</p>
<div class="editor-field">
#Html.DropDownListFor(model => model.TeamName, Model.Teams, "Choose Team", new { #class = "selectstyle" })
#Html.ValidationMessageFor(model => model.ContactPerson)
</div>
I would do the following in jquery:
$('#PlayerName').change(function()
{
var id = $(this).val()
$.ajax({
url: '#Url.Action("GetPositions")',
data:
{
"id": id,
},
success: function (data)
{
$("#positions").html(data);
}
});
}
Then i will have in the action controller:
public JsonResult GetPositions(int id)
{
var positions = Repository.GetPositionsByPlayerId(id);
var hmtl = positions.Select(x => new SelectListItem
{
Value = x.PositionID.ToString(),
Text = x.Name
});
return new JsonResult { Data = html };
}
This will fill the values with all the postions related to that player.
You could subscribe to the .change() event of the first dropdown and then based on the currently selected value enable/disable the others:
$(function() {
$('#PlayerName').change(function() {
var value = $(this).val();
if (value == 'Player3') {
$('#PositionName, #TeamName').removeAttr('disabled');
} else if (value == 'Player2') {
$('#PositionName').removeAttr('disabled');
$('#TeamName').attr('disabled', 'disabled');
}
});
});

Resources