Extra param from jquery autocomplete to mvc controller - c#

I require some help with my JQuery autocomplete.
Im trying to autocomplete postalcodes filtered by countries but for this I need to send a country value along with the autocomplete term to my mvc controller.
The value of the var country should be send as an extra param towards the controller action but I can't seem to accomplish this.
Here is my setup without the extra param:
View:
#Html.TextBoxFor(j => j.MainAddressCountry, new { data_autocomplete_url = Url.Action("AutoCompletePostalCode", "Location")})
script:
var country = $("#MainAddressCountry");
$('*[data-autocomplete-url]')
.each(function() {
$(this).autocomplete({
source: $(this).data("autocomplete-url"),
messages: {
noResults: '',
results: function() {
}
},
focus: function() {
var menu = $(this).data("uiAutocomplete").menu.element;
var focused = menu.find("li:has(a.ui-state-focus)");
if (menu.parent().hasClass('scroll-wrapper')) {
setTimeout(function() {
var height = menu.parent().height();
if (focused.position().top + focused.height() > height) {
menu.scrollTop(menu.scrollTop() + parseInt(focused.position().top) + focused.height() - height);
}
}, 1);
}
},
"open": function() {
var menu = $(this).data("uiAutocomplete").menu.element;
menu.addClass('scrollbar-dynamic').addClass('autocomplete-scroll');
}
});
}
);
controller:
public ActionResult AutoCompletePostalCode(string term)
{
var items = new[] {"2220", "2222", "1800", "1900", "3541", "5214", "9000", "9002", "9006"};
var filteredItems = items.Where(
item => item.IndexOf(term, StringComparison.InvariantCultureIgnoreCase) >= 0
);
return Json(filteredItems, JsonRequestBehavior.AllowGet);
}
Anyone that might know how to do the trick?
Thanks in advance.

You need to change the way you build source property into something like this:
source: function(request, response) {
$.getJSON("url here", { parameter1: 'your first parameter', parameter2: 'your second parameter' },
response);
},
Controller action:
public JsonResult Test(string parameter1, string parameter2)
{
//code
}

Related

how to get SelectList from another Ienumerable

Ihave IEnumerable named (JournalArticleTypes ) the elements inside this IEnumerable will help me to access some records of a table named (articletypes) the condition is that when the foreign key (ArticleTypeId) of any element of IEnumerable is being equal to any Id of any record from the table (articletypes) then i will save the record that have this Id in a new SelectList named (articletypes )...so i will use first IEnumerableto get select list from specifc table(articletypes)...how to do it?
I tried by using this wrong code
[AllowAnonymous]
public JsonResult GetArticleType(int journalid)
{
var JournalArticleTypes = _context.JournalArticleTypes.Where(u => u.JournalId == journalid);
foreach(var item in JournalArticleTypes)
{
var articletypes = new SelectList(_context.ArticleTypes.Where(u => u.Id == item.ArticleTypeId), "Id", "Name");
}
return Json(articletypes);
}
Ajax code:
<script>
$("#joartcltypeid").empty();
$(document).ready(function () {
$("#jornlid").change(function () {
$("#joartcltypeid").empty();
$("#joartcltypeid").append($('<option>', { text: "#Localizer["إختر نوع المقالة"]" }));
if ($("#jornlid").val() > 0) {
var JournalOptions = {};
JournalOptions.url = "/#CultureInfo.CurrentCulture.Name/JournalRepositories/GetArticleType/";
JournalOptions.data = { journalid: $("#jornlid").val() };
JournalOptions.success = function (data) {
$.each(data, function (index, row) {
$("#joartcltypeid").append($('<option>', { value: row.value, text: row.text }))
});
};
JournalOptions.error = function () { alert("#Localizer["UniError"]"); };
$.ajax(JournalOptions);
}
});
});
</script>

MVC5 Controller: Check for duplicate in DB before saving?

On my View I have a button I use to submit a [description] value to my Controller via JSON, which is then used to create a new Table record. For example:
[HttpPost]
public JsonResult createNewStatus(string description)
{
INV_Statuses status = new INV_Statuses()
{
// ID auto-set during save
status_description = description,
created_date = DateTime.Now,
created_by = System.Environment.UserName
};
//var allErrors = ModelState.Values.SelectMany(x => x.Errors);
try
{
if (ModelState.IsValid)
{
db.INV_Statuses.Add(status);
db.SaveChanges();
}
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
return Json(new { ID = status.Id, Text = status.status_description }, JsonRequestBehavior.AllowGet);
}
What I'd like to do now (before saving the Status to the DB) is run a check to see if any other records in the INV_Statuses table have a [description] value matching the one submitted to the function for new creation. If there is a match, I want to return an error/validation? message and alert the user the submitted value already exists and to choose it from the DropDownList on the View.
Can anyone provide an example of how to go about this with LINQ in my MVC Controller?
EDIT: Added my View JS code for submitting the new Status:
$('#createNewStatus').click(function () {
$('#createStatusFormContainer').show();
})
$('#cancelNewStatus').click(function () {
$('#createStatusFormContainer').hide();
})
$('#submitNewStatus').click(function () {
var form = $(this).closest('form');
var data = { description: document.getElementById('textNewStatus').value };
$.ajax({
type: "POST",
dataType: "JSON",
url: '#Url.Action("createNewStatus", "INV_Assets")',
data: data,
success: function (resp) {
$('#selectStatus').append($('<option></option>').val(resp.ID).text(resp.Text));
form[0].reset();
$('#createStatusFormContainer').hide();
var count = $('#selectStatus option').size();
$("#selectStatus").prop('selectedIndex', count - 1);
},
error: function () {
alert("ERROR!");
}
});
});
EDIT2:
Adricadar's suggestion:
INV_Statuses status = new INV_Statuses()
{
// ID auto-set during save
status_description = description,
created_date = DateTime.Now,
created_by = System.Environment.UserName
};
try
{
var existingStatus = db.INV_Statuses.FirstOrDefault(x => x.status_description.ToUpper() == status.status_description.ToUpper());
var isDuplicateDescription = existingStatus != null;
if (isDuplicateDescription)
{
ModelState.AddModelError("Error", "[" + status.status_description + "] already exists in the database. Please select from the DropDownList.");
}
else if (ModelState.IsValid)
{
db.INV_Statuses.Add(status);
db.SaveChanges();
}
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
return Json(new { ID = status.Id, Text = status.status_description }, JsonRequestBehavior.AllowGet);
I added a .ToUpper() in my comparison in Controller, but even though the match with .ToUpper() gets identified, the ModelState.AddModelError() code fires, then the code returns and no error message is issued?
The value (though duplicate) still gets added to the dropdownlist (visually, not in DB) via my current JS code:
$('#createNewStatus').click(function () {
$('#createStatusFormContainer').show();
})
$('#cancelNewStatus').click(function () {
$('#createStatusFormContainer').hide();
})
$('#submitNewStatus').click(function () {
var form = $(this).closest('form');
var data = { description: document.getElementById('textNewStatus').value };
$.ajax({
type: "POST",
dataType: "JSON",
url: '#Url.Action("createNewStatus", "INV_Assets")',
data: data,
success: function (resp) {
$('#selectStatus').append($('<option></option>').val(resp.ID).text(resp.Text));
form[0].reset();
$('#createStatusFormContainer').hide();
var count = $('#selectStatus option').size();
$("#selectStatus").prop('selectedIndex', count - 1);
},
error: function () {
alert("ERROR!");
}
});
});
Check for existing status and set status back as follows:
var existingStatus = db.INV_Statuses.FirstOrDefault(s => s.status_description == description);
if (existingStatus ==null)
{
db.INV_Statuses.Add(status);
db.SaveChanges();
}
else
{
// set the status back to existing
status = existingStatus;
}
Set an existing flag in your response:
return Json(new { ID = status.Id, Text = status.status_description, AlreadyExists = (existingStatus != null) }, JsonRequestBehavior.AllowGet);
Then in your response JavaScript, simply parse out the returned data:
success: function (resp) {
if (resp.AlreadyExists != true)
{
$('#selectStatus').append($('<option></option>').val(resp.ID).text(resp.Text));
form[0].reset();
$('#createStatusFormContainer').hide();
var count = $('#selectStatus option').size();
$("#selectStatus").prop('selectedIndex', count - 1);
}
else
{
alert(resp.status_description + " already exists");
$("#selectStatus").val(resp.Id);
}
}
You can query the database for a status with an existing description and if exists and an model state error.
Be aware that string comparison is case sensitive.
[HttpPost]
public JsonResult createNewStatus(string description)
{
INV_Statuses status = new INV_Statuses()
{
// ID auto-set during save
status_description = description,
created_date = DateTime.Now,
created_by = System.Environment.UserName
};
//var allErrors = ModelState.Values.SelectMany(x => x.Errors);
try
{
var existingStatus = db.INV_Statuses.FirstOrDefault(x => x.status_description.ToUpper() == status.status_description.ToUpper());
var isDuplicateDescription = existingStatus != null;
string error = String.Empty;
if (isDuplicateDescription)
{
error = "[" + status.status_description + "] already exists in the database. Please select from the DropDownList.";
}
else if (ModelState.IsValid)
{
db.INV_Statuses.Add(status);
db.SaveChanges();
}
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
return Json(new { ID = status.Id, Text = status.status_description, Error = error , IsDuplicate = isDuplicateDescription }, JsonRequestBehavior.AllowGet);
}
In javascript verify if response have IsDuplicate = true if is true you skip the part where you need to add an element in dropdown.
$('#createNewStatus').click(function () {
$('#createStatusFormContainer').show();
})
$('#cancelNewStatus').click(function () {
$('#createStatusFormContainer').hide();
})
$('#submitNewStatus').click(function () {
var form = $(this).closest('form');
var data = { description: document.getElementById('textNewStatus').value };
$.ajax({
type: "POST",
dataType: "JSON",
url: '#Url.Action("createNewStatus", "INV_Assets")',
data: data,
success: function (resp) {
if(resp.IsDuplicate)
{
//display error from response
//display resp.Error
} else {
$('#selectStatus').append($('<option></option>').val(resp.ID).text(resp.Text));
form[0].reset();
$('#createStatusFormContainer').hide();
var count = $('#selectStatus option').size();
$("#selectStatus").prop('selectedIndex', count - 1);
}
},
error: function () {
alert("ERROR!");
}
});
});

Typeahead.js and Bloodhound.js integration with C# WebForms WebMethod

I'm trying to implement a simple WebMethod in C# to search a db of 50,000 people. I'm using Twitter Bootstrap bloodhound.js and typeahead.js to tokenize and autocomplete the responses.
When I run this code, typeahead shows a dropdown menu with undefined.
How can I correctly process the JSON response to strip out the d object returned by .NET WebMethod and correctly pass my records to Bloodhound? I've tried this using the dataFilter method provided by jQuery's $.ajax, but it's not working for me.
C# WebMethod:
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public static object searchStaffByName(string q)
{
using (App_Data.DQDBDataContext dc = new App_Data.DQDBDataContext())
{
var results = dc.getStaffDetails(q).ToList();
return new { Status = "OK", Records = results, Count = results.Count };
}
}
Typeahead JS implementation:
var textlookup = new Bloodhound({
datumTokenizer: function (d) {
return Bloodhound.tokenizers.whitespace(d.val);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: 'Search.aspx/searchStaffByName',
replace: function (url, query) {
searchQuery = query;
return url;
},
ajax: {
beforeSend: function (jqXhr, settings) {
settings.data = JSON.stringify({
q: searchQuery
});
jqXhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
},
dataFilter: function (data, type) {
if (type === "json") {
data = $.parseJSON(data);
if (typeof data.d === 'object' && data.d.Count > 0) {
return data.d.Records;
}
}
},
type: 'POST'
}
}
});
textlookup.initialize();
$('.autocomplete').typeahead({
hint: true,
highlight: true,
minLength: 3
},
{
name: 'textlookup',
displayKey: 'Forename',
source: textlookup.ttAdapter()
});
Sample JSON Response:
{
"d": {
"Status":"OK",
"Records": [{
"id":45711192,
"phone":"514-579-5721",
"Forename":"Jayden",
"Surname":"Adams",
"DOB":"\/Date(990226800000)\/"
},
{
"id":12603644,
"phone":"333-143-9094",
"Forename":"Jake",
"Surname":"Adams",
"DOB":"\/Date(43542000000)\/"
},
{
"id":68196438,
"phone":"440-505-2403",
"Forename":"Josh",
"Surname":"Adams",
"DOB":"\/Date(-51152400000)\/"
}],
"Count":6
}
}
If your typeahead data will be in the name: 'textlookup', array, populate the array with your JSON response first. The following assumes data is the JSON.
textlookup = [];
for (var i = 0; i < data.d.Records.length; i += 1) {
textlookup.push(data.d.Records[i].Forename);
}
This should push each Forename into the array textlookup. You are getting the undefined error because you are placing objects into the array.
I spent some time on this and found that it's better to return a an string array.
Here's my web method.
[WebMethod]
public static string[] MemberLookup(string MbrFullName)
{
DataSet ds = (dataset provider goes here)
List<string> members = new List<string>();
foreach(DataRow dr in ds.Tables[0].Rows)
{ members.Add(string.Format("{0}-{1}", dr["label"].ToString(), dr["value"].ToString())); }
return members.ToArray();
}

Can't set selected value in select2 plugin in Bootstrap

I use a select2 plugin my website. I can't set selected value in select2. Please see my code below.
Html
<input id="drpEditProvider" class="form-control" type="text" value="" tabindex="8" name="ProviderId" data-required="true" />
Script
var attendeeUrl = '#Url.Action("GetProvider", "Admin")';
var pageSize = 100;
$('#drpEditProvider').select2(
{
placeholder: 'Please Select Provider',
//Does the user have to enter any data before sending the ajax request
minimumInputLength: 0,
allowClear: true,
//tags:["red", "green", "blue"],
ajax: {
////How long the user has to pause their typing before sending the next request
//quietMillis: 150,
//The url of the json service
url: attendeeUrl,
dataType: 'jsonp',
//Our search term and what page we are on
data: function (term, page) {
return {
pageSize: pageSize,
pageNum: page,
searchTerm: term
};
},
results: function (data, page) {
//Used to determine whether or not there are more results available,
//and if requests for more data should be sent in the infinite scrolling
var more = (page * pageSize) < data.Total;
return { results: data.Results, more: more };
}
},
initSelection: function (element, callback) {
var data = [];
$(element.val().split(",")).each(function () {
data.push({ id: this, text: this });
});
callback(data);
},
});
Controller and model
public class Select2PagedResult
{
public int Total { get; set; }
public List<Select2Result> Results { get; set; }
}
public class Select2Result
{
public string id { get; set; }
public string text { get; set; }
}
public JsonResult GetProvider(string searchTerm, int pageSize, int pageNum)
{
int Count = 0;
List<Provider> provideres = ProviderHelper.GetAllProvider(searchTerm, out Count);
//Translate the attendees into a format the select2 dropdown expects
Select2PagedResult pagedProvider = new Select2PagedResult();
pagedProvider.Results = new List<Select2Result>();
//Loop through our attendees and translate it into a text value and an id for the select list
foreach (Provider a in provideres)
{
pagedProvider.Results.Add(new Select2Result { id = a.Id.ToString(), text = a.Name });
}
//Set the total count of the results from the query.
pagedProvider.Total = Count;
//Return the data as a jsonp result
return new JsonpResult
{
Data = pagedProvider,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
Finally I found the answer. You can set selected value in select2 plugin like below.
Select 2 Single selection
$("#drpselector").select2("data", { id: "1", text:"Test 1" });
Select 2 Multiple selection
var arrdata = "1:Test 1,2:Test 2"
var thdprdata = [];
$(arrdata.split(",")).each(function () {
thdprdata.push({ id: this.split(':')[0], text: this.split(':')[1] });});
$("#drpselector").select2("data", thdprdata);
I use this code in my application. It works fine for me.

Grid not updating after ajaxcall

I'm creating my own filter. The filter has a dropdownlist, when this dropdownlist changes jquery will make an ajaxcall to the corresponding HTTPPOST - controller action. In this action I filter the list and pass it through to my view.
Once the list has reached my view, the webgrid is not updating. Here is some code with some debug information to show what's happening.
Controller
normal action
public ActionResult Projects()
{
IEnumerable<Project> projects = Adapter.ProjectRepository.Get();
int test = projects.Count();
List<ProjectsDisplayViewmodel> projectsView = new List<ProjectsDisplayViewmodel>();
string strCats = "";
foreach (Project prj in projects)
{
strCats = "";
Mapper.CreateMap<Project, ProjectsDisplayViewmodel>();
ProjectsDisplayViewmodel newProject = Mapper.Map<Project, ProjectsDisplayViewmodel>(prj);
foreach (Category cat in prj.Categories)
{
strCats += cat.CategoryName + ", ";
}
newProject.strCategories = strCats;
projectsView.Add(newProject);
}
ViewBag.Categories = new SelectList(Adapter.CategoryRepository.Get(), "CategoryID", "CategoryName");
/*projectsview contains 4 projects now*/
return View(projectsView);
}
httppost action
[HttpPost]
public ActionResult Projects(string catID, string strSearch)
{
IEnumerable<Project> projects = Adapter.ProjectRepository
.Get()
.Where(x =>
x.Categories.Any(
c =>
c.CategoryID == Convert.ToInt16(19))
);
int test = projects.Count();
List<ProjectsDisplayViewmodel> projectsView = new List<ProjectsDisplayViewmodel>();
string strCats = "";
foreach (Project prj in projects)
{
strCats = "";
Mapper.CreateMap<Project, ProjectsDisplayViewmodel>();
ProjectsDisplayViewmodel newProject = Mapper.Map<Project, ProjectsDisplayViewmodel>(prj);
foreach (Category cat in prj.Categories)
{
strCats += cat.CategoryName + ", ";
}
newProject.strCategories = strCats;
projectsView.Add(newProject);
}
ViewBag.Categories = new SelectList(Adapter.CategoryRepository.Get(), "CategoryID", "CategoryName");
/*projectsview contains 1 project now AND WILL DISPLAY 4*/
return View(projectsView);
}
project.cshtml
#model IEnumerable<Freelauncher.Models.ProjectsDisplayViewmodel>
#{
ViewBag.Title = "Projects";
}
#{
var grid = new WebGrid(Model, canPage: true, rowsPerPage: 25,
selectionFieldName: "selectedRow",ajaxUpdateContainerId: "gridContent");
grid.Pager(WebGridPagerModes.NextPrevious);
}
<h2>Alle projecten</h2>
#Html.DropDownList("Categories", (SelectList) ViewBag.Categories)
<div id="gridContent">
#grid.GetHtml(
columns: grid.Columns(
grid.Column("ProjectTitle", "Titel"),
grid.Column("ProjectDeadlineDate", "project deadline"),
grid.Column("ProjectEndRegisterDate", "Registreer deadline"),
grid.Column("ProjectBudget", "Prijs"),
grid.Column("ProjectIsFixedPrice", "Vaste prijs"),
grid.Column("strCategories","Categorieën"),
grid.Column("ProjectID", "meer info", format: (item) => Html.ActionLink("info", "project", new { Id = item.ProjectID} ))
//grid.Column("ProjectID", "meer info", format: Html.ActionLink("info", "project", new { Id = }
))
</div>
What am I missing that the project list in my view is not updated, the correct data is passed to the view....
edit
The ajax call
$("#Categories").change(function () {
var param = $(this).val();
$.ajax({
type: "POST",
url: "/Project/Projects",
data: { catID: $(this).val(), strSearch: 'test' },
dataType: "json",
success: function (response) { console.log("succes"); },
error: function (xhr, ajaxOptions, thrownError) {console.log("error"); }
});
});
I just noticed that nothing is printed in my console.log.... Not the success neither the error function.
You don't seem to be doing anything in the success callback of your AJAX request (other than a console.log call). So it's perfectly normal that nothing updates. You need to manually update your DOM if you want this to happen.
So you should modify your HttpPost controller action so that it returns a PartialView containing the grid. And then in your success callback inject the newly received partial into the DOM:
success: function (response) {
$('#some_id_of_a_containing_div').html(response);
console.log("succes");
},
In this example you should wrap the partial view into a containing div which will get updated here:
<div id="some_id_of_a_containing_div">
#Html.Partial("Partial_Containing_Your_Grid")
</div>

Categories