Intercept and pre-process jQuery-ui autocomplete data - javascript

I have a Jquery UI autocomplete code that grabs data from an ajax request, as i grab the data the results are already put in the input box where the autocomplete is attached. my problem is i have a other data along the data that will be posted with the result of the autocomplete.
I had tried to grab all the i need and put it in a single string with delimeters so i can split() it on the client-side. I want to save the other data in a hidden text field
here is my code
<div id="01ac091c834d81b41f0ef4b6eb09cde90bb9aa1a" style="display:none" title="Add Member">
Type the name of the member
<br>
<br>
<div style="text-align:center">
<input type="text" id="txtUserFind" size="35">
</div>
<input type="hidden" id="hidtxtUserFind-nickname">
<input type="hidden" id="hidtxtUserFind-userhash">
<input type="hidden" id="hidtxtUserFind-picture">
<input type="hidden" id="hidtxtUserFind-sex">
</div>
<script type="text/javascript">
head(function() {
$(":button:contains('Select User')").attr("disabled","disabled").addClass("ui-state-disabled");
$("#txtUserFind").keydown(function(){
$(":button:contains('Select User')").attr("disabled","disabled").addClass("ui-state-disabled");
});
$("#txtUserFind").change(function(){
var userdetails = $("#txtUserFind").val().split(";");
alert($("#txtUserFind").val());
/*
0 profiles.nickname,
1 profiles.firstname,
2 profiles.surname,
3 users.user_hash,
4 profiles.sex,
5 profiles.picture
*/
$("input#hidtxtUserFind-nickname").val(userdetails[0]);
$("input#txtUserFind").val(userdetails[1] + " " + userdetails[2]);
$("input#hidtxtUserFind-userhash").val(userdetails[3].replace("u-",""));
$("input#hidtxtUserFind-sex").val(userdetails[4]);
if(userdetails.length > 5){
$("input#hidtxtUserFind-picture").val(userdetails[5]);
}
});
$("<?php echo $tagmemberbtn; ?>").click(function(){
$("#01ac091c834d81b41f0ef4b6eb09cde90bb9aa1a").dialog({
modal:true,
resizable: false,
height:250,
width:400,
hide:"fade",
open: function(event, ui){
searchdone = false;
$(":button:contains('Select User')").attr("disabled","disabled").addClass("ui-state-disabled");
},
beforeClose: function(event, ui){
$("#txtUserFind").val("");
},
buttons:{
"Select User":function(){
$(this).dialog("close");
},
"Close":function(){
searchdone = false;
$("#txtUserFind").val("");
$(this).dialog("close");
}
}
});
});
$(function() {
var cache = {},
lastXhr;
$("#txtUserFind").autocomplete({
source:function(request,response) {
var term = request.term;
if ( term in cache ) {
response(cache[term]);
return;
}
lastXhr = $.getJSON(cvars.userburl+"getusers", request, function(data,status,xhr) {
stopAllAjaxRequest();
cache[ term ] = data;
if ( xhr === lastXhr ) {
response( data );
}
});
},
minLength: 1,
select: function(event, ui) {
$(":button:contains('Select User')").removeAttr("disabled").removeClass("ui-state-disabled");
}
}).data("autocomplete")._renderItem = function(ul,item){
if(item.picture==null){
//know if girl or boy
if(item.sex<=0){
item.picture = cvars.cthemeimg + "noimagemale.jpg";
}
else{
item.picture = cvars.cthemeimg + "noimagefemale.jpg";
}
}
else{
item.picture = cvars.gresuser + "hash=" + item.user_hash.replace("u-","") +"&file="+item.picture.replace("f-","");
}
var inner_html = '<a><div class="autocomplete-users-list_item_container"><div class="autocomplete-users-image"><img src="' + item.picture + '" height="35" width="35"></div><div class="label">' + item.nickname + '</div><div class="autocomplete-users-description">' + item.firstname + " " + item.surname + '</div></div></a>';
return $("<li></li>")
.data("item.autocomplete",item)
.append(inner_html)
.appendTo(ul);
};
});
});

You idea is right, you must use a callback as the source parameter. I've put together an example here:
Demo on jsFiddle
If you read the documentation carefully it says:
The third variation, the callback, provides the most flexibility, and
can be used to connect any data source to Autocomplete. The callback
gets two arguments:
A request object, with a single property called "term", which refers
to the value currently in the text input. For example, when the user
entered "new yo" in a city field, the Autocomplete term will equal
"new yo".
A response callback, which expects a single argument to contain the
data to suggest to the user. This data should be filtered based on the
provided term, and can be in any of the formats described above for
simple local data (String-Array or Object-Array with label/value/both
properties). It's important when providing a custom source callback to
handle errors during the request. You must always call the response
callback even if you encounter an error. This ensures that the widget
always has the correct state.
So here is an example implementation I used in the demo:
$("#autocomplete").autocomplete({
source: function(request, response) {
$.ajax({
url: "/echo/html/", // path to your script
type: "POST", // change if your script looks at query string
data: { // change variables that your script expects
q: request.term
},
success: function(data) {
// this is where the "data" is processed
// for simplicity lets assume that data is a
// comma separated string where first value is
// the other value, rest is autocomplete data
// the data could also be JSON, XML, etc
var values = data.split(",");
$("<div/>").text("Other value: " + values.shift()).appendTo("body");
response(values);
},
error: function() {
response([]); // remember to call response() even if ajax failed
}
});
}
});

You can include a function on select. Within that function you can access the value and the label of the selected item and process as needed:
$('#input_id').autocomplete({
source:"www.example.com/somesuch",
select: function(event, ui){
var value = ui.item.value;
valueArray = value.split('whatever delimiter here');
//do what you will with the values
ui.item.value = ui.item.label; //This ensures only the label is displayed after processing.
}
});

Related

ajax postback method for refreshing dropdown list

Scoop...
I have a drop down list that might not display a particular option you're looking for. I added a button with pop up modal to type in a field you want to add to the drop down list. It functions perfectly, but I need to add an ajax postback method to refresh the list after the user hits enter. I don't want to refresh the whole page, just the list. any help?
Controller:
public ActionResult AddLeadSource()
{
return View();
}
[HttpPost]
public ActionResult AddLeadSource(string name)
{
LeadSource ls = new LeadSource();
ls.Name = name;
db.LeadSources.Add(ls);
db.SaveChanges();
return Json(new { success = true });
}
JS
<script>
$("#AddNew").change(function () {
var name = $("#Name").val();
// var order = $("#DisplayOrder").val();
$.ajax({
type: 'POST',
dataType: 'json',
cache: false,
url: '/Admin/LeadSource/AddLeadSource',
data: { name: name },
success: function (response) {
//alert("Success " + response.success);
$('#FollowUpNotes').kendoWindow('destroy');
// Refresh the DropDown <-- Heres where I need some help!
},
error: function (jqXHR, textStatus, errorThrown) {
alert('Error - ' + errorThrown);
}
});
});
In your success function of your Ajax call add this:
$("IdOfDropDownList").data("kendoDropDownList").dataSource.read();
In this way your dropdownlist will call the read function and reload all data. I assumed that your dropdownlist is binding throught read call.
I highly recommend looking at jQuery UI's autocomplete widget. That said,
$('#YourDropDownID option').remove(); //this will remove all option elements inside the <select id="YourDropDownID">
Then you just need to build new ones based on the response data,
for (var o in data) {
if (data[o].Value != undefined) {
$('#YourDropDownID').append('<option value="' + data[o].Value + '">' + ("" + data[o].Display) + '</option>');
}
}
I do this inside the .done() callback of my AJAX:
.done(function (data) {
//above code
}
Depending on the nature of the data you are sending back you may need to loop through it differently. Mine is an array of objects with a Value and Display properties (in my case, account numbers and account names).
//server side controller
var query = #"
Select
SubString([mn_no], 0, 6) As Value,
RTRIM([acct_desc]) As Display
From [some_table]";
return con.Query(query, new { AccountNumber = accounts.Select(x =>
{
return new { Value = x.Value, Display = x.Display };
});

How to represent and autocomplete JSON of suggestions based on Google Books API?

This question is based on Results of recommendations using Google Books API are irrelevant .
In general I am building possibility for user to add the book to his collection.
For this purpose user searches through books using information from Google Books. But without suggestions based on what user types in the search field, it would be extremely uncomfortable.
At this point now we get jSON text of book suggestions, but I do not really understand how to represent this? So how to create a normal list of that JSON and create possibility for user to choose one of those recommendations,so that each of them will be autocompleted in the search field on click?
var requestUrl = "https://suggestqueries.google.com/complete/search?client=chrome&ds=bo&q=";
var xhr;
$(document).on("input", "#query", function () {
typewatch(function () {
var queryTerm = $("#query").val();
$("#indicator").show();
if (xhr != null) xhr.abort();
xhr = $.ajax({
url: requestUrl + queryTerm,
dataType: "jsonp",
success: function (response) {
$("#indicator").hide();
$("#output").html(response);
}
});
}, 500);
});
$(document).ready(function () {
$("#indicator").hide();
});
var typewatch = (function () {
var timer = 0;
return function (callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<input type=text id="query" placeholder="Start typing..." /><span id="indicator"></span>
<div style="width:600px;height:700px;padding-bottom:100px;position:relative;background:#6c94b8;" id="output"></div>
<label for="query" style="position:relative;margin-left:100px;margin-top:100px;">Tags: </label>
</div>
I think this is what you are after:
https://www.librarieshacked.org/tutorials/autocompletewithapi
$(document).ready(function () { // only begin once page has loaded
$("#txtBookSearch").autocomplete({ // attach auto-complete functionality to textbox
// define source of the data
source: function (request, response) {
// url link to google books, including text entered by user (request.term)
var booksUrl = "https://www.googleapis.com/books/v1/volumes?printType=books&q=" + encodeURIComponent(request.term);
$.ajax({
url: booksUrl,
dataType: "jsonp",
success: function(data) {
response($.map(data.items, function (item) {
if (item.volumeInfo.authors && item.volumeInfo.title && item.volumeInfo.industryIdentifiers && item.volumeInfo.publishedDate)
{
return {
// label value will be shown in the suggestions
label: item.volumeInfo.title + ', ' + item.volumeInfo.authors[0] + ', ' + item.volumeInfo.publishedDate,
// value is what gets put in the textbox once an item selected
value: item.volumeInfo.title,
// other individual values to use later
title: item.volumeInfo.title,
author: item.volumeInfo.authors[0],
isbn: item.volumeInfo.industryIdentifiers,
publishedDate: item.volumeInfo.publishedDate,
image: (item.volumeInfo.imageLinks == null ? "" : item.volumeInfo.imageLinks.thumbnail),
description: item.volumeInfo.description,
};
}
}));
}
});
},
select: function (event, ui) {
// what to do when an item is selected
// first clear anything that may already be in the description
$('#divDescription').empty();
// we get the currently selected item using ui.item
// show a pic if we have one
if (item.image != '')
{
$('#divDescription').append('<img src="' + ui.item.image + '" style="float: left; padding: 10px;">');
}
// and title, author, and year
$('#divDescription').append('<p><b>Title:</b> ' + ui.item.title + '</p>');
$('#divDescription').append('<p><b>Author:</b> ' + ui.item.author + '</p>');
$('#divDescription').append('<p><b>First published year:</b> ' + ui.item.publishedDate + '</p>');
// and the usual description of the book
$('#divDescription').append('<p><b>Description:</b> ' + ui.item.description + '</p>');
// and show the link to oclc (if we have an isbn number)
if (ui.item.isbn && ui.item.isbn[0].identifier)
{
$('#divDescription').append('<P><b>ISBN:</b> ' + ui.item.isbn[0].identifier + '</p>');
$('#divDescription').append('View item on worldcat');
}
},
minLength: 2 // set minimum length of text the user must enter
});
});

Getting and Setting a data attribute in HTML

I have an edit box, defined like this:
<input class="change-handled form-control" type-id="#sub.CategoryTypeId" sub-category-id="#sub.SubCategoryId" data-id="#sub.CategoryBudgetId" style="text-align: right; width: 100%" type="number" value="#(sub.BudgetAmount.HasValue ? sub.BudgetAmount.ToString() : "")" />
In Javascript, I get the data-id value successfully like this:
var dataId = $(this).attr('data-id');
I now need to set it to a different value. I am trying:
$(this).setAttribute("data-id", 5);
But it seems the data-id never gets set to the value I pass. How can I set the data-id value of my editbox?
Full code of the function being used. (Note, no error checking yet):
$('body').on('change', 'input.change-handled', UpdateTotals);
function UpdateTotals() {
var dataId = $(this).attr('data-id');
var categoryId = $(this).attr('sub-category-id');
var value = $(this).val();
var totalExp = 0;
var totalInc = 0;
var $changeInputs = $('input.change-handled');
$changeInputs.each(function (idx, el) {
if ($(el).attr('type-id') == 2) {
totalInc += Number($(el).val());
}
if ($(el).attr('type-id') == 1) {
totalExp += Number($(el).val());
}
});
$(this).val(numberWithCommas(value));
$('#budgettedExpenseValue').text(numberWithCommas(totalExp));
$('#budgettedIncomeValue').text(numberWithCommas(totalInc));
$('#budgettedAvailableValue').text(numberWithCommas(totalInc - totalExp));
$.ajax({
url: '#Url.Action("SaveBudgetValue", "Budget")',
type: "POST",
contentType: "application/json",
data: JSON.stringify({ budgetCategoryId: dataId, catgoryId: categoryId, month: 4, year: 2015, value: value }),
cache: false,
async: true,
success: function (result) {
if (result.Success == 'true') {
$(this).attr("data-id", result.Id);
alert("Saved! " + result.Id.toString());
} else {
alert("Failed");
}
},
error: function () {
alert("Oh no...");
}
});
The code, after an edit box of the class type is edited, sums up all the income boxes (Decorated with a type-id = 1), and updates a field, and all the expense boxes (type-id = 2) and updates a separate field.
It then saves the data with a json call to my controller. If it's a new entry, data-id would have been NULL. The save method returns the primary key of the value saved. That value is displayed in my alert, and is supposed to be assigned to the edit boxe's data-id. But - isn't.
Re your update
The problem is that in the ajax success callback, this doesn't refer to the element anymore.
Two ways to fix that, and a third way that will be available in ES6:
Assign this, or more usefully $(this), to a variable that you use in the success handler (and elsewhere, no need to constantly call $() repeatedly on the same element):
function UpdateTotals() {
var input = $(this); // <========== Save it here
// ...
$.ajax({
// ...
success: function (result) {
// ...
if (result.Success == 'true') {
input.attr("data-id", result.Id); // <========= Use it here
alert("Saved! " + result.Id.toString());
} else {
alert("Failed");
}
}
});
// ...
}
Use Function#bind (an ES5 feature, but it can be shimmed for really old browsers) to make this within the callback the same as this outside it:
function UpdateTotals() {
// ...
$.ajax({
// ...
success: function (result) {
// ...
if (result.Success == 'true') {
$(this).attr("data-id", result.Id);
alert("Saved! " + result.Id.toString());
} else {
alert("Failed");
}
}.bind(this) // <=========== Note
});
// ...
}
In ES6, we'll have arrow functions, which unlike normal functions inherit the this of the context in which they're created. So in ES6, you could do this:
// **ES6 ONLY**
function UpdateTotals() {
// ...
$.ajax({
// ...
success: (result) => { // <==== Arrow function syntax
// ...
if (result.Success == 'true') {
$(this).attr("data-id", result.Id);
alert("Saved! " + result.Id.toString());
} else {
alert("Failed");
}
}
});
// ...
}
I'd lean toward #1, because you're doing a lot of repeated $(this) anyway, so just as well to do var input = $(this); once and then use input throughout.
More about this on my blog:
You must remember this
Original answer pre-update:
Since you're using jQuery, you set an attribute with attr, like this:
$(this).attr("data-id", 5);
Live Example:
var input = $("input");
snippet.log("Before: " + input.attr("data-id"));
input.attr("data-id", 5);
snippet.log("After (jQuery): " + input.attr("data-id"));
snippet.log("After (DOM): " + input[0].getAttribute("data-id"));
snippet.log("Element's HTML (after): " + input[0].outerHTML);
<input class="change-handled form-control" type-id="#sub.CategoryTypeId" sub-category-id="#sub.SubCategoryId" data-id="#sub.CategoryBudgetId" style="text-align: right; width: 100%" type="number" value="#(sub.BudgetAmount.HasValue ? sub.BudgetAmount.ToString() : "")" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Or you can just use the DOM directly, by not wrapping the element in a jQuery wrapper:
this.setAttribute("data-id", 5);
Note that in either case, even though you're giving a number as the value, the value will end up being a string (as attributes only store strings).
You'll get people telling you to use data, but data is not just a way to access data-* attributes, even though many people make that mistake. It might be useful for your end goal, though, depending on what that is. The jQuery data function manages a cache of data that it associates with the element. The values data manages are initialized from data-* attributes, but data never writes to data-* attributes. If you're just trying to update attribute values, use attr. If you're trying to do something more complex and it's not important that the values get written back to the element as attributes, look at the docs for data and see whether it might be useful for you. (For one thing, the values data manages can be types other than strings.)
You are putting your code in the success function where this will be some sort of jQuery ajax object and not an HTML element.
You need to store this in a variable outside the ajax call and then use that variable.
e.g.
var that = this;
$.ajax({
Then:
setAttribute is a DOM method, not a jQuery method.
Either:
$(that).attr("data-id", 5);
or (assuming this is a Element object):
that.setAttribute("data-id", 5);

Mystery ajax request occurring somehow

Whenever I type in the autocomplete field an ajax request is sent and there is no code I've written to do this. Checking the console I see it's a 400 GET request to the controller that loaded this view with param (json) appended to the url. I'm absolutely stumped.
<head>
<script data-main="<?=base_url()?>public/requirejs/main.js" src="<?=base_url()?>public/requirejs/require-jquery.js"></script>
<script>
requirejs(['a_mod'],
function(a_mod) {
$(document).ready(function() {
var param = [];
param = $('#elem').attr('value');
a_mod.foo(param, "#someElem");
});
});
<script>
main.js
require(["jquery",
"jquery-ui"],
function() {
}
);
The autocomplete function
'foo' : function(param, elementAutocomplete, elementTags) {
console.log("init ac");
$(elementAutocomplete).autocomplete({
source: param,
minLength: 1,
select: function (event, ui) {
event.preventDefault();
//
}
}).data( "autocomplete" )._renderItem = function( ul, item ) {
return $("<li></li>")
.data( "item.autocomplete", item )
.append( '<a>' + item.label + '</a>' )
.appendTo(ul);
}
},
Your source attribute for the autocompleter is a string:
param = $('#elem').attr('value');
And a string source means that it is a URL:
Autocomplete can be customized to work with various data sources, by just specifying the source option. A data source can be:
an Array with local data
a String, specifying a URL
a Callback
Saying var param = []; just means that param is initialized as an empty array, it doesn't mean that param will always be an array. You need to fix your param value to be an array.

jQuery UI AutoComplete: Only allow selected valued from suggested list

I am implementing jQuery UI Autocomplete and am wondering if there is any way to only allow a selection from the suggested results that are returned as opposed to allowing any value to be input into the text box.
I am using this for a tagging system much like the one used on this site, so I only want to allow users to select tags from a pre-populated list returned to the autocomplete plugin.
You could also use this:
change: function(event,ui){
$(this).val((ui.item ? ui.item.id : ""));
}
The only drawback I've seen to this is that even if the user enters the full value of an acceptable item, when they move focus from the textfield it will delete the value and they'll have to do it again. The only way they'd be able to enter a value is by selecting it from the list.
Don't know if that matters to you or not.
I got the same problem with selected not being defined. Got a work-around for it and added the toLowerCase function, just to be safe.
$('#' + specificInput).autocomplete({
create: function () {
$(this).data('ui-autocomplete')._renderItem = function (ul, item) {
$(ul).addClass('for_' + specificInput); //usefull for multiple autocomplete fields
return $('<li data-id = "' + item.id + '">' + item.value + '</li>').appendTo(ul);
};
},
change:
function( event, ui ){
var selfInput = $(this); //stores the input field
if ( !ui.item ) {
var writtenItem = new RegExp("^" + $.ui.autocomplete.escapeRegex($(this).val().toLowerCase()) + "$", "i"), valid = false;
$('ul.for_' + specificInput).children("li").each(function() {
if($(this).text().toLowerCase().match(writtenItem)) {
this.selected = valid = true;
selfInput.val($(this).text()); // shows the item's name from the autocomplete
selfInput.next('span').text('(Existing)');
selfInput.data('id', $(this).data('id'));
return false;
}
});
if (!valid) {
selfInput.next('span').text('(New)');
selfInput.data('id', -1);
}
}
}
http://jsfiddle.net/pxfunc/j3AN7/
var validOptions = ["Bold", "Normal", "Default", "100", "200"]
previousValue = "";
$('#ac').autocomplete({
autoFocus: true,
source: validOptions
}).keyup(function() {
var isValid = false;
for (i in validOptions) {
if (validOptions[i].toLowerCase().match(this.value.toLowerCase())) {
isValid = true;
}
}
if (!isValid) {
this.value = previousValue
} else {
previousValue = this.value;
}
});
This is how I did it with a list of settlements:
$("#settlement").autocomplete({
source:settlements,
change: function( event, ui ) {
val = $(this).val();
exists = $.inArray(val,settlements);
if (exists<0) {
$(this).val("");
return false;
}
}
});
i just modify to code in my case & it's working
selectFirst: true,
change: function (event, ui) {
if (ui.item == null){
//here is null if entered value is not match in suggestion list
$(this).val((ui.item ? ui.item.id : ""));
}
}
you can try
Ajax submission and handling
This will be of use to some of you out there:
$('#INPUT_ID').autocomplete({
source: function (request, response) {
$.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
url: autocompleteURL,
data: "{'data':'" + $('INPUT_ID').val() + "'}",
dataType: 'json',
success: function (data) {
response(data.d);
},
error: function (data) {
console.log('No match.')
}
});
},
change: function (event, ui) {
var opt = $(this).val();
$.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
url: autocompleteURL,
data: "{'empName':'" + name + "'}",
dataType: 'json',
success: function (data) {
if (data.d.length == 0) {
$('#INPUT_ID').val('');
alert('Option must be selected from the list.');
} else if (data.d[0] != opt) {
$('#INPUT_ID').val('');
alert('Option must be selected from the list.');
}
},
error: function (data) {
$(this).val('');
console.log('Error retrieving options.');
}
});
}
});
I'm on drupal 7.38 and
to only allow input from select-box in autocomplete
you only need to delete the user-input at the point,
where js does not need it any more - which is the case,
as soon as the search-results arrive in the suggestion-popup
right there you can savely set:
**this.input.value = ''**
see below in the extract from autocomplete.js ...
So I copied the whole Drupal.jsAC.prototype.found object
into my custom module and added it to the desired form
with
$form['#attached']['js'][] = array(
'type' => 'file',
'data' => 'sites/all/modules/<modulname>_autocomplete.js',
);
And here's the extract from drupal's original misc/autocomplete.js
modified by that single line...
Drupal.jsAC.prototype.found = function (matches) {
// If no value in the textfield, do not show the popup.
if (!this.input.value.length) {
return false;
}
// === just added one single line below ===
this.input.value = '';
// Prepare matches.
=cut. . . . . .
If you would like to restrict the user to picking a recommendation from the autocomplete list, try defining the close function like this. The close function is called when the results drop down closes, if the user selected from the list, then event.currentTarget is defined, if not, then the results drop down closed without the user selecting an option. If they do not select an option, then I reset the input to blank.
//
// Extend Autocomplete
//
$.widget( "ui.autocomplete", $.ui.autocomplete, {
options: {
close: function( event, ui ) {
if (typeof event.currentTarget == 'undefined') {
$(this).val("");
}
}
}
});
You can actually use the response event in combination to the change event to store the suggested items like so:
response: function (event, ui) {
var list = ui.content.map(o => o.value.toLowerCase());
},
change: function (event, ui) {
if (!ui.item && list.indexOf($(this).val().toLowerCase()) === -1 ) { $(this).val('');
}

Categories

Resources