I am using "jQuery UI Autocomplete" to filter the JSON data, I am using following code for it
<input type="text" name="searchKeywords" id="ninecSearchKeywords" placeholder="How To...">
and then JS as
jQuery(document).ready(function($){
$.getJSON("apiUrl", function(data){
$.each(data, function(key, value){
$("#ninecSearchKeywords").autocomplete({
source: value.keys,
autoFocus: true,
select: function (event, ui) {
window.location = value.pageLink;
}
});
});
});
});
and JSON Data is
[
{
pageID: "454",
pageLink: "url",
sectionID: "a599d36c4c7a71ddcc1bc7259a15ac3a",
anchorLink: "anchor1",
keys: [
"Result 1",
"Result 2",
"Result 3"
]
},
{
pageID: "455",
pageLink: "url",
sectionID: "8d993394c892dcaa8683dc4ba4fae21d",
anchorLink: "anchor2",
keys: [
"Result 4",
"Result 5",
"Result 6"
]
},
{
pageID: "456",
pageLink: "url",
sectionID: "dce6920a3408ae9a8e61b75a4e5fd6ca",
anchorLink: "anchor3",
keys: [
"Result 7",
"Result 8",
"Result 9"
]
}
]
This is working fine for #2 and #3 iteration and I am able to search for Result 4 to Result 9 but for the first iteration i.e. Result 1,2 and 3 no results are showing in dropdown.
Can anyone knows what went wrong here?
The autocomplete source will not get updated inside loop. Create the data first and then add the source to autocomplete.
jQuery(document).ready(function($){
$.getJSON("apiUrl", function(data){
var keys = [];
var keyLinkMap = {};
$.each(data, function(key, value){
for(let i = 0; i <= value.keys.length; i++) {
keys.push({label: value.keys[i], value:value.pageLink});
}
});
$("#ninecSearchKeywords").autocomplete({
source: keys,
autoFocus: true,
select: function (event, ui) {
window.location = value.pageLink;
}
});
});
});
Related
A company email-generating application I'm working on has an autocomplete input to autofill email theme data into a form. The data is returned as a JSON object, but a couple of the object values extraps and extraul contain multidimensional arrays. I'm able to get the plain key:value data just fine out in the response, but I can't seem to figure out how to pull the arrays in so I can loop over them to update certain sections of the form.
Here's a look at some of the JSON code coming in:
0:
emaildate: "2019-01-10"
extraps: Array(2)
0: {extrap: "test paragraph", position: 1}
1: {extrap: "another paragraph", position: 3}
length: 2
__proto__: Array(0)
extraul: Array(4)
0: {ulid: 1, position: 2, li: "list item 1", liposition: 1}
1: {ulid: 1, position: 2, li: "list item 2", liposition: 2}
2: {ulid: 1, position: 2, li: "list item something new", liposition: 3}
3: {ulid: 1, position: 2, li: "A new list item", liposition: 4}
length: 4
__proto__: Array(0)
id: 44
label: "Some Kind of Email Theme - 2019-01-10"
lastupdated: "2019-01-06 02:00:04"
store: "Premier"
themedesc: "Here's a description of the theme."
themeimage: null
themeincludeextrap: 1
themeincludeul: 1
themelink: "some-kind-of-email-theme"
themelinkinclude: 1
themename: "Some Kind of Email Theme"
themenotes: "Some notes about it"
themesortorder: 0
value: "Some Kind of Email Theme"
__proto__: Object
length: 1
__proto__: Array(0)
And here's a look at the javascript to bring it in from autotheme.php:
//Autofill Theme Info based on text entry
$( "#themename" ).autocomplete({
source: function( request, response ) {
$.ajax({
url: "autotheme.php",
type: "GET",
dataType: "json",
data: {
q: request.term
},
success: function(data) {
console.log(data);
response($.map(data, function(item) {
return {
id: item.id,
value: item.value,
label: item.label,
themename: item.themename,
themenotes: item.themenotes,
themedesc: item.themedesc,
themeimage: item.themeimage,
themeincludeextrap: item.themeincludeextrap,
themeincludeul: item.themeincludeul,
themelinkinclude: item.themelinkinclude,
themelink: item.themelink,
themeextraps: item.extraps,
themeextraul: item.extraul
}
}))
},
error: function(errorThrown){
console.log(errorThrown);
console.log("There is an error with theme autocomplete.");
}
});
},
minLength: 2,
select: function(event, ui) {
if (ui.item) {
$this = $(this);
$('#themeid').val('');
$('#extratext').html('');
$('#themename').val(ui.item.themename);
$('#themenotes').val(ui.item.themenotes);
$('#themedesc').val(ui.item.themedesc);
var themeimage = ui.item.themeimage;
var themeincludeextrap = ui.item.themeincludeextrap;
var themeincludeul = ui.item.themeincludeul;
var themelinkinclude = ui.item.themelinkinclude;
var themeextraps = ui.item.extraps;
var themeextraul = ui.item.extraul;
if(themeextraps !== undefined) {
var extrapcount = themeextraps.length;
}
if(themeextraul !== undefined) {
var extraulcount = themeextraul.length;
}
if((themeextraps !== undefined) || (themeextraul !== undefined)) {
var extratextpositions = {};
$.each(themeextraps, function(i, themeextraps) {
extratextpositions[themeextraps.position] = 'p';
})
$.each(themeextraul, function(i, themeextraul) {
extratextpositions[themeextraul.position] = 'ul';
})
$.each(extratextpositions, function(key, value) {
if(extratextpositions[key] == 'p') {
addExtraP.call(this);
} else {
addExtraUl.call(this);
}
});
$('#themelink').val(ui.item.themelink);
if(themelinkinclude == 1) {
$('#themelinkinclude').prop("checked", true);
} else {
$('#themelinkinclude').prop("checked", false);
}
event.preventDefault();
}
},
open: function(event, ui) {
$(".ui-autocomplete").css("z-index", 1000);
},
complete: function(){
$("#themename").removeClass("ui-autocomplete-loading");
}
}
});
I'm able to get the simple key:value values just fine, but I get undefined for the arrays. I'm sure there's a different way I need to pull those in, but I don't know how and can't seem to find the answer in other threads on here. Any help would be greatly appreciated!
Figured out a way to get around this problem, thanks to some help from #Bibberty. I'm not sure if this is the most graceful or easy way to solve it, but it worked for me. I created an array from the JSON data values, then created variables from the arrays within the data array and added them to the response return value. Here's the new functional code (or, at least, the part that matters):
//Autofill Theme Info based on text entry
$( "#themename" ).autocomplete({
source: function( request, response ) {
$.ajax({
url: "autotheme.php",
type: "GET",
dataType: "json",
data: {
q: request.term
},
success: function(data) {
const results = data.map(function (value, label) {
return [value];
})
var extraps = results[0][0]['extraps'];
var extraul = results[0][0]['extraul'];
response($.map(data, function(item) {
return {
id: item.id,
value: item.value,
label: item.label,
themename: item.themename,
themenotes: item.themenotes,
themedesc: item.themedesc,
themeimage: item.themeimage,
themeincludeextrap: item.themeincludeextrap,
themeincludeul: item.themeincludeul,
themelinkinclude: item.themelinkinclude,
themelink: item.themelink,
extraps: extraps,
extraul: extraul
}
}))
$("#themename").removeClass("ui-autocomplete-loading");
},
error: function(errorThrown){
console.log(errorThrown);
console.log("There is an error with theme autocomplete.");
}
});
},
minLength: 2,
select: function(event, ui) {
if (ui.item) {
$this = $(this);
console.log(ui.item.extraps);
$('#themeid').val('');
$('#extratext').html('');
$('#themename').val(ui.item.themename);
$('#themenotes').val(ui.item.themenotes);
$('#themedesc').val(ui.item.themedesc);
var themeimage = ui.item.themeimage;
var themeincludeextrap = ui.item.themeincludeextrap;
var themeincludeul = ui.item.themeincludeul;
var themelinkinclude = ui.item.themelinkinclude;
var themeextraps = ui.item.extraps;
var themeextraul = ui.item.extraul;
if(themeextraps !== undefined) {
var extrapcount = themeextraps.length;
}
if(themeextraul !== undefined) {
var extraulcount = themeextraul.length;
}
...
I have an API that is called by Select2 (v4.0.5) however the debug message in the console says:
Select2: The AJAX results did not return an array in the results key of the response.
When I review the documentation at Select2's documentation site I seem to be following it correctly. This is the javascript I use on the webpage:
$('#account_id').select2({
debug: true,
minimumInputLength: 3,
dataType: 'json',
ajax: {
url: '/api/account-query',
data: function (params) {
var query = {
search: params.term,
v: "new"
}
return query;
},
}
});
This is the response from the API (sensitive bits redacted):
{
"results": [{
"id": "redacted-1",
"text": "text redacted 1"
},{
"id": "redacted-2",
"text": "text redacted 2"
},{
"id": "redacted-3",
"text": "text redacted 3"
},{
"id": "redacted-4",
"text": "text redacted 4"
},{
"id": "redacted-5",
"text": "text redacted 5"
}]
}
If I take the select2 code and supply it with the static json response (without results prepended, just the array) it works just fine.
What am I missing?
Thanks!
You have to provide the processResults callback function so Select2 able to render result in proper way.
I have created a jsfiddle (https://jsfiddle.net/shcavbng/) demo that doing the same.
$('#account_id').select2({
debug: true,
minimumInputLength: 3,
dataType: 'json',
ajax: {
url: 'https://reqres.in/api/users',
data: function (params) {
console.log('params =>' , params);
var query = {
search: params.term,
v: "new"
}
return query;
},
processResults: function (data) {
console.log('results =>' , data);
data = data.data.reduce(function(o,i){o.push({id:i.id,text:i.first_name});return o;},[]);
console.log('results =>' , data);
return {
results: data
};
}
}
});
I figured it out, and as expected I was overlooking a simple aspect:
Content-Type header needed to be application/json vs text/html
I have a json named names.json and I need to do an input with autocomplete that looks for 'name' values inside the json.
How do I do that?
I Tried:
$(function () {
var getData = function (request, response) {
$.getJSON(
"/cities.json" + request.term,
function (data) {
response(data);
});
};
var selectItem = function (event, ui) {
$("#myInput").val(ui.item.value);
return false;
}
$("#myInput").autocomplete({
source: getData,
select: selectItem,
minLength: 2,
change: function() {
$("#myInput").val("").css("display", 2);
}
});
});
But I'm doing something wrong in my code.
I get a json from an external file
JSON is exactly from this format, I need to return the values of 'names' when I type in the input:
[
{
"id":25,
"name":"locale name test 2",
"state_id":6
},
{
"id":667,
"name":"locale name test 3",
"state_id":24
},
{
"id":331,
"name":"locale name test 4",
"state_id":13
},
{
"id":776,
"name":"locale name test 5",
"state_id":26
},
{
"id":680,
"name":"locale name test 6",
"state_id":26
}
]
Here is base working autocomplete example based on the data you gave.
HTML:
<input type="text" id="suggestion" />
Jquery:
var data = [
{
"id":25,
"name":"locale name test 2",
"state_id":6
},
{
"id":667,
"name":"locale name test 3",
"state_id":24
},
{
"id":331,
"name":"locale name test 4",
"state_id":13
},
{
"id":776,
"name":"locale name test 5",
"state_id":26
},
{
"id":680,
"name":"locale name test 6",
"state_id":26
}
]
var data_arr = data.map(function(val){
return val.name;
}). //get all the val.names on an array to make
// it easier when it comes setting autocomplete source
console.log(data_arr)
$("#suggestion").autocomplete({
source: data_arr,
minLength: 2
});
Here is the working version of the above code hosted on jsFiddle
In addition:
if you have to get the data from external source, here is how I would do it
HTML:
<input type="text" id="suggestion" />
Jquery:
// I have hosted the same data you provided on myjson.com
$.getJSON( "https://api.myjson.com/bins/1gkh25", function( data ) {
var data_arr = data.map(function(val){
return val.name;
})
auto(data_arr)
});
function auto(data_arr){
$("#suggestion").autocomplete({
source: data_arr,
minLength: 2
});
}
try it on jsFiddle
I am trying to filter results using Typeahead.js. I can currently filter the results using a field called activity_title. This works fine.
How can I filter my results by a second value? In this case, I would like to select only the results that have a certain value for activity_level. I need to set this when the typeahead is initialised rather than hard coding it into the Bloodhound initialisation (e.g. I don't want to use url: 'api/activity/&range=1,3')
I have the following valid JSON that I access remotely:
{
"meta": [
{
"name": "activity_id",
"table": "table",
"max_length": 4
},
{
"name": "activity_title",
"table": "table",
"max_length": 91
},
{
"name": "activity_level",
"table": "table",
"max_length": 2
}
],
"detail": [
{
"activity_id": "57",
"activity_title": "Help old ladies to cross the road.",
"activity_level": "2"
},
{
"activity_id": "58",
"activity_title": "Help mum with the washing up.",
"activity_level": "3"
},
{
"activity_id": "59",
"activity_title": "Shine my shoes",
"activity_level": "1"
},
{
"activity_id": "60",
"activity_title": "Put the bins out",
"activity_level": "1"
}
]
}
I set up a Bloodhound instance like this:
var activities = new Bloodhound({
datumTokenizer: function (datum) {
return Bloodhound.tokenizers.whitespace(datum.activity_title);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: '/api/activity/',
filter: function(data) {
return $.map(data['detail'], function(detail) {
return {
activity_id: detail.activity_id,
activity_title: detail.activity_title,
objective_level: detail.objective_level
};
});
}
}
});
I use Typeahead.js to do a lookup on the data as I type.
$( document ).on( "focus", ".typeahead-init", function() {
// + '&range=' + minimum + ',' + maximum
var minimum = $('#group-level-min-1').val();
var maximum = $('#group-level-max-1').val();
$(this).typeahead({
highlight: true
},
{
name: 'activity_title',
displayKey: 'activity',
source: activities.ttAdapter(),
templates: {
header: '<div class="header-name">Activities</div>',
empty: [
'<div class="empty-message">',
'No activities match your search',
'</div>'
].join('\n'),
suggestion: Handlebars.compile('<div class="typeahead-activity" id="typeahead-activity-{{activity_id}}"><strong>{{objective_level}}</strong> - {{activity_title}}</div>')
}
})
//info on binding selection at https://github.com/twitter/typeahead.js/issues/300
.bind('typeahead:selected', function(obj, datum, name) {
var target = $(this).closest('.activity-container');
var activityId = datum['activity_id'];
var url = '/api/activity/id/'+activityId;
$(target).children('.activity-id').val(activityId);
//http://runnable.com/UllA9u8MD5wiAACj/how-to-combine-json-with-handlebars-js-for-javascript-ajax-and-jquery
var raw_template = $('#activity-output').html();
// Compile that into an handlebars template
var template = Handlebars.compile(raw_template);
// Fetch all data from server in JSON
$.get(url,function(data,status,xhr){
$.each(data,function(index,element){
// Generate the HTML for each post
var html = template(element);
// Render the posts into the page
target.append(html);
});
});
});
$(this).removeClass("typeahead-init");
$(this).focus();
});
This has been cobbled together from several answers on Stackoverflow and others. Any help greatly appreciated.
JSfiddle: http://jsfiddle.net/cjVSj/
I have a simple select2 with the range of possible tags set by the tags option and the preloaded tags set by values in the input field in the html.
When the on change event fires on the select2, the removed item seems to lose its id, reporting instead its text value.
To see the problem, adding a tag (e.g. west) correctly reports the added.id, but removing the existing east tags reports id = east, not 1356.
Any insight into how to gain access to the id of a tag upon removal?
HTML:
<script>
var tags = [{ "id": 1354, "text": "north", "restricted": false
}, {"id": 1355, "text": "south", "restricted": false
}, {"id": 1356, "text": "east", "restricted": false
}, {"id": 1357, "text": "west", "restricted": false
}];
</script>tags:
<input type="text" id="mytags" value="east" />
JS:
$(document).ready(function () {
$('#mytags').select2({
placeholder: 'Search',
allowClear: true,
minimumInputLength: 2,
multiple: true,
tags: tags,
tokenSeparators: [','],
});
$('#mytags').on("change", function (e) {
console.log("change " + JSON.stringify({
val: e.val,
added: e.added,
removed: e.removed
}));
if (e.added) {
alert('added: ' + e.added.text + ' id ' + e.added.id)
} else if (e.removed) {
alert('removed: ' + e.removed.text + ' id ' + e.removed.id)
}
});
});
There was an issue with your select2 declaration and syntax.
Further more, if you entered any other text, say "eas" or "test", your piece of code reflected that as it is. Check this scenario.
Updated fiddle: http://jsfiddle.net/ZBf5H/
To be specific, you did not give appropriate mapping to your tags. Please find how to access remote data in select 2 from here
The change of code is as below:
$(document).ready(function() {
var data=[{id:1354,text:'north',restricted:false},
{id:1356,text:'east',restricted:false},
{id:1357,text:'west',restricted:false},
{id:1355,text:'south',restricted:false}];
function format(item)
{ return item.text; }
$('#mytags').select2({
placeholder: 'Search',
allowClear: true,
minimumInputLength: 2,
multiple: true,
tags: tags,
tokenSeparators: [','],
data:{ results: data, text: 'text' },
formatSelection: format,
formatResult: format
});
Let me know if this works for you.
Ok... I've got a working solution, but I still don't exactly understand the difference between select2's tags and data options....
JSfiddle: http://jsfiddle.net/7e8Pa/
I'm initializing select2 with a list of all possible tags via the data option from an array, then selecting those for preloading: the initSelection function checks for ids in the and looks them up in the data array (the pre-stored one, not Select2's). Last, new tags may be added (the createSearchChoice does this). To hook this to my server, I'm just going to insert ajax calls where noted below in the on-change event handler (which gets called after createSearchChoice, and can overwrite the field values for the new object set in createSearchChoice).
JS:
function findWithAttr(array, attr, value) {
for (var i = 0; i < array.length; i += 1) {
if (array[i][attr] == value) {
return array[i];
}
}
}
$(document).ready(function () {
function format(item) {
return item.text;
}
$('#mytags').select2({
placeholder: 'Search',
minimumInputLength: 2,
multiple: true,
//tags: tags,
tokenSeparators: [','],
data: {
results: tags,
text: 'text'
},
initSelection: function (element, callback) {
var data = [];
$($('#mytags').val().split(",")).each(function (i) {
var o = findWithAttr(tags, 'id', this);
if (o) {
data.push({
id: o.id,
text: o.text
});
} else {
console.log("findWithAttr returned none; likely invalid id");
}
});
console.log("data = " + JSON.stringify(data));
callback(data);
},
createSearchChoice: function (term, data) {
console.log("create");
if ($(data).filter(function () {
return this.text.localeCompare(term) === 0;
}).length === 0) {
// call $.post() to add this term to the server, receive back id
// return {id:id, text:term}
// or detect this shiftiness and do it below in the on-change
return {
id: -1,
text: term
};
}
},
formatSelection: format,
formatResult: format
});
$('#mytags').on("change", function (e) {
console.log("change " + JSON.stringify({
val: e.val,
added: e.added,
removed: e.removed
}));
if (e.added) {
alert('added: ' + e.added.text + ' id ' + e.added.id);
//modifying the id here overrides what is assigned above in createSelection
e.added.id = 5;
} else if (e.removed) {
alert('removed: ' + e.removed.text + ' id ' + e.removed.id);
}
var selections = (JSON.stringify($('#mytags').select2('data')));
$('#selectedText').text(selections);
});
});
HTML:
<script>
var tags = [{
"id": 1354,
"text": "north",
"restricted": false
}, {
"id": 1355,
"text": "south",
"restricted": false
}, {
"id": 1356,
"text": "east",
"restricted": false
}, {
"id": 1357,
"text": "west",
"restricted": false
}];
</script>
<p>tags:
<input type="text" id="mytags" value="1355" style="width:80%" />
</p>
<p>Selected Options: <span id="selectedText"></span>
</p>
<p>Debug: <span id="debug"></span>
</p>