Typeahead shows results as undefined - javascript

I am trying to use typeahead to display google suggestions.
The Ajax call works fine and data is returned properly:
Before executing return process(data);
data contains an array of strings that start with "w".
data = ["walmart", "weather", "wells fargo", "worldstarhiphop",
"walgreens", "wikipedia", "white pages", "world cup", "webmd",
"weather radar"]
However the suggestions displayed show "undefined" instead of real words.
Any idea what I am missing here? Thanks.
<input type="text" class="typeahead" placeholder="Search">
$('.typeahead').typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
source: function (query, process) {
$.getJSON("Home/Suggest", { query: query }, function (data) {
return process(data);
});
}
});

Update:
After some research, I found an answer to my question and will post it here, if someone needs it.
The trick is - the "process" callback function expects the results in a format:
[{value: "string1"}, {value: "string2"}, {value: "string3"}]
and not just an array of strings.
$('.typeahead').typeahead(
{ hint: true, highlight: true, minLength: 1 }, // options
{
source: function (query, process) { // source dataset, data = array of strings
$.getJSON('Home/Suggest', { query: query }, function (data) {
//data=["string1", "string2", "string3"]
//process callback function needs it
//in a format [{value: "string1"}, {value: "string2"}, {value: "string3"}]
var output = $.map(data, function (string) { return { value: string }; });
process(output);
});
}
});

Related

Results does not show up after search, why?

I am using the newest Select2 4.0.6-rc.1 and I am making an AJAX search, I am getting results back but for some reason they don't show up as an option on the SELECT element. Maybe I am missing some basic setup but I have been reading the docs and I can't find anything helpful. Here is what I have:
<select class="form-control" id="query"></select>
$(function() {
var $query = $("#query");
$query.select2({
width: 550,
placeholder: 'Search for a form ...',
minimumInputLength: 3,
ajax: {
url: '/ajax/forms/ajax_search_by_name',
data: function (params) {
return {
q: params.term
};
},
dataType: 'json',
delay: 250,
processResults: function (data) {
return {
results: data.items
};
}
}
});
});
The result from the backend looks like:
{
"results": [
{
"id": 1247,
"name": "amerita_infusion_services_colorado_synagis_referral_form"
},
{
"id": 3471,
"name": "medicaid_colorado_viekira"
}
]
}
Which I think is the right way to return them. I have setup a Fiddle - which BTW I couldn't make it to work with the /echo/json request - so you can play with it a little bit.
What I am missing here?

javascript kendo scheduler with json data moves events only once

I have a kendo scheduler and I'm having a problem with move event. I can move only one event at a time and then I can't even grab any event afterwards. I think there's something wrong with the dates but I really can't figure out why. I tried dataSource with 2-3 events and it's working but when I put that exact same data in php and return it as json. It's not working.
Any help would be appreciated.
$("#scheduler").kendoScheduler({
date: new Date(),
startTime: new Date(today2()),
timezone: "Etc/UTC",
currentTimeMarker: false,
height: 800,
views:
[
"week",
{ type: "month", selected: true, eventHeight: 60}
],
dataSource:
{
transport:
{
read:
{
url: "tasks.php",
dataType: "json"
},
batch: true,
parameterMap: function (options, operation)
{
if (operation === "read") {
var scheduler = $("#scheduler").data("kendoScheduler");
var result =
{
start: scheduler.view().startDate(),
end: scheduler.view().endDate()
}
return kendo.stringify(result);
}
return kendo.stringify(options);
}
},
schema:
{
model:
{
id: "taskId",
fields:
{
taskId: { type: "number", from: "TT_CODE" },
start: { type: "date", from: "TT_START_DATETIME"},
end: { type: "date", from: "TT_END_DATETIME"},
title: { from: "TT_EDIT"}
}
}
}
}
});
php file with json data:
$json[0]['TT_CODE'] = 1;
$json[0]['TT_START_DATETIME'] = "2016-01-16 15:00:00";
$json[0]['TT_END_DATETIME']= "2016-01-16 16:00:00";
$json[0]['TT_EDIT'] = "Fast and furious 6";
echo json_encode($json);
Since you specified start and end as date in your model, the scheduler is expecting to received properly formatted date. While JSON passes date as string, javascript still expect the date string to be formatted.
In your case, it would be:
$json[0]['TT_CODE'] = 1;
$json[0]['TT_START_DATETIME'] = "2016-01-16T15:00:00.000Z";
$json[0]['TT_END_DATETIME']= "2016-01-16T16:00:00.000Z";
$json[0]['TT_EDIT'] = "Fast and furious 6";
You could also leave date as is and handle the string with a custom logic in the parameterMap function.
As a side note, if you have a doubt about the type formatting, go in the browser console and have a look at the network tab. You'll be able to compare the data sent by the working service to the data sent by PHP and see where is the difference.

TypeAhead.js and Bloodhound showing an odd number of results

I have a TypeAhead/Bloodhound implementation in my frontend, that fetches JSON-data from a Play/Scala-server. Typeahead-version is 0.11.1. The implementation is as follows:
HTML:
<div id="typeahead" class="col-md-8">
<input class="typeahead form-control" type="text" placeholder="Select the user">
</div>
JavaScript:
var engine = new Bloodhound({
datumTokenizer: function (datum) {
var fullName = fullName(datum);
return Bloodhound.tokenizers.whitespace(fullName);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
identify: function(obj) { return obj.id; },
remote: {
url: routes.controllers.Users.index("").url,
cache: false,
replace: function (url, query) {
if (!isEmpty(query)) {
url += encodeURIComponent(query);
}
return url;
},
filter: function (data) {
console.log(data);
return $.map(data, function (user) {
return {
id: user.id,
fullName: viewModel.fullName(user)
};
});
}
}
});
// instantiate the typeahead UI
$('#typeahead .typeahead')
.typeahead({
hint: true,
highlight: true,
minLength: 1,
},
{
name: 'engine',
displayKey: 'fullName',
source: engine
})
function fullName(data) {
if (data === undefined) return "";
else {
var fName = data.firstName === undefined ? "" : data.firstName;
var lName = data.lastName === undefined ? "" : data.lastName;
return fName + ' ' + lName;
}
};
JSON-response the server gives:
[{"firstName":"Test","lastName":"User", "id":1}, ... ]
The server pages the result so that it gives maximum of 5 results, which is supposed to be the default limit for Typeahead/Bloodhound as well.
The problem is that when the server returns 5 results, Typeahead shows 0 results in the overlay. If the server gives 4 results, TypeAhead shows 1 in the overlay. If the server gives 3 results, TypeAhead shows 2 results. For 2 and 1 results it shows the correct number of elements in the overlay. If I remove the page length and the server returns over 10 results, then TypeAhead shows 5 results (the limit). console.log inside the filter shows the correct number of data-results, so they go to Bloodhound at least.
What might be the issue with this code? This TypeAhead-field is the only TypeAhead-field present in this page. I checked the DOM, and TypeAhead generates wrong amount of result-set fields, so it's not a problem with CSS (tried to remove all custom CSS as well).
Thanks for any replies :)
Twitter supposedly abandoned the project. Try the maintained fork. It has the issue fixed.
There is an issue on typeahead in the code:
https://github.com/twitter/typeahead.js/issues/1218
You can change the code yourself in the JS as described on the issue page.
Try initializing engine with engine.initialize() ; returning suggestion object at filter , which should be available at templates -> suggestion ; initializing engine at source with source:engine.ttAdapter(); returning element as String at suggestion , to populate "suggestion" drop down menu . See Typeahead - examples - Custom Templates
var data = [{
"firstName": "Test",
"lastName": "User",
"id": 1
}, {
"firstName": "Test2",
"lastName": "User2",
"id": 2
}, {
"firstName": "Test3",
"lastName": "User3",
"id": 3
}, {
"firstName": "Test4",
"lastName": "User4",
"id": 4
}, {
"firstName": "Test5",
"lastName": "User5",
"id": 5
}];
var engine = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace("value"),
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: $.map(data, function(val, key) {
// return `suggestion` object for `templates` `suggestion`
return {value:val.firstName, suggestion:val}
})
});
// initialize `engine`
engine.initialize();
// instantiate the typeahead UI
$("#typeahead .typeahead")
.typeahead({
hint: true,
highlight: true,
minLength: 1,
}, {
name: "engine",
displayKey: "firstName",
templates: {
suggestion: function (data) {
// `suggestion` object passed at `engine`
console.log(data.suggestion);
// return suggestion element to display
var _suggestion = "<div>"
+ data.suggestion.firstName
+ " "
+ data.suggestion.lastName + "</div>";
return _suggestion
}
},
source: engine.ttAdapter()
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://twitter.github.io/typeahead.js/releases/latest/typeahead.bundle.js"></script>
<div id="typeahead" class="col-md-8">
<input class="typeahead form-control" type="text" placeholder="Select the user">
</div>
If (ike me) you don't want to go into the typeahead source, (for instance because you want to use the min version) you can also set limit very high. You will then have to limit the number of items to put into the list yourself.
$('#typeahead .typeahead')
.typeahead({
hint: true,
highlight: true,
minLength: 1,
},
{
name: 'engine',
displayKey: 'fullName',
source: engine,
limit: 1000
})
For anyone who finds this, use the maintained version of the code. The original is crap.
https://github.com/corejavascript/typeahead.js

Use different value from JSON data instead of displayKey using Typeahead

I have started using Typeahead.js and am struggling to figure out a way of allowing a user to type and search for a company name, once selected input the associated company code.
.json file:
[{
"company_name": "Facebook",
"code": "fb",
}, {
"company_name": "Google",
"code": "goog",
}, {
"company_name": "Yahoo",
"code": "yhoo",
}, {
"company_name": "Apple",
"code": "aapl",
}, {
"company_name": "Royal Mail",
"code": "rmg.l",
}]
.js Script:
var stocks = new Bloodhound({
datumTokenizer: function(d) {
return Bloodhound.tokenizers.whitespace(d.code);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
limit: 3,
prefetch: {
url: 'javascripts/stockCodes.json',
filter: function(list) {
return $.map(list, function(stock) {
return {
code: stock
};
});
}
}
});
stocks.initialize();
$('.typeahead').typeahead(null, {
name: 'stocks',
displayKey: 'code',
source: stocks.ttAdapter()
});
Currently, this just displays the list of codes when the user types in the input field. However, I would like to know if there is a way to allow them to search on code but once selected, the value in the textbox to be company_name? Is this even possible using this plugin. Any help will be greatly appreciated.
Thanks!
displayKey is used to indicate which key should be shown in the dropdown list and in the input field after the user has selected an entry.
If you want the entries shown in the dropdown list to be different from what ends up in the input field you need to use a custom template.
From the documentation:
suggestion – Used to render a single suggestion. If set, this has to be a
precompiled template. The associated suggestion object will serve as the
context. Defaults to the value of displayKey wrapped in a p tag i.e.
<p>{{value}}</p>.
Something like this should work:
$('.typeahead').typeahead(null, {
name: 'stocks',
displayKey: 'company_name',
source: stocks.ttAdapter(),
templates: {
suggestion: function (stock) {
return '<p>' + stock.code + '</p>';
}
}
});
The submitted template will be used to show stock.code in the dropdown list, while stock.company_name will be shown in the input field after the user selects an entry.
I am no jQuery / JavaScript-Guru. So I prefer it the easy way. Just to understand what I did when I look at my code later...
Thanks to Eric Saupe I found a smart solution:
//JSON FILE
[
{
"company_name": "Facebook",
"code": "fb",
},
{
"company_name": "Google",
"code": "goog",
},
{
"company_name": "Yahoo",
"code": "yhoo",
},
{
"company_name": "Apple",
"code": "aapl",
},
{
"company_name": "Royal Mail",
"code": "rmg.l",
},
]
// JS SCRIPT
var stocks = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('company_name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: 'javascripts/stockCodes.json'
});
stocks.initialize();
$('.typeahead').typeahead(
null, {
name: 'stocks',
displayKey: 'company_name',
source: stocks.ttAdapter()
}).on('typeahead:selected', function(event, data){
$('.typeahead').val(data.code);
});
Just use the typeahead custom events as described in the docs on github.
Hope, this helps (and when it's just for my own later reference).
JSFIDDLE
You need to use the ´displayKey´ variable. See below copy-paste from the typeahead docs:
displayKey – For a given suggestion object, determines the string representation of it. This will be used when setting the value of the input control after a suggestion is selected. Can be either a key string or a function that transforms a suggestion object into a string. Defaults to value.
https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md
Your specific code would then be something like this:
var stocks = new Bloodhound({
datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.code); },
queryTokenizer: Bloodhound.tokenizers.whitespace,
limit: 3,
prefetch: {
url: 'javascripts/stockCodes.json',
filter: function(list) {
// This should not be required, but I have left it incase you still need some sort of filtering on your server response
return $.map(list, function(stock) { return { code: stock.code, company_name: stock.company_name }; });
}
}
});
stocks.initialize();
$('.typeahead').typeahead(null, {
name: 'stocks',
displayKey: function(stock) {
return stock.company_name;
},
source: stocks.ttAdapter()
});
I had a very similar requirement on my own site, and I had this working with no issue. I have not tried this particular example though, so let me know if it works.
Remember to call stocks.clearPrefetchCache(); to clear your cache to easier track bugs.
If I read correctly, I believe this is what you want:
var stocksData = [{
"Facebook": "fb",
}, {
"Google": "goog",
}, {
"Yahoo": "yhoo",
}, {
"Apple": "aapl",
}, {
"Royal Mail": "rmg.l",
}, ];
var stocks = new Bloodhound({
datumTokenizer: function (d) {
for (var prop in d) {
return Bloodhound.tokenizers.whitespace(d[prop]);
}
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
limit: 3,
local: stocksData,
});
stocks.initialize();
$('input').typeahead(null, {
name: 'stocks',
displayKey: function(stocks) {
for (var prop in stocks) {
return prop;
}
},
source: stocks.ttAdapter()
});
You will of course want to change the local to prefetch/remote like you had for the json file.
UPDATE
FIDDLE
Had the same problem, could not get it working with the display or displayKey property. Worked around it by adding the name property to the source objects:
$.each(stocks, function (i, item) {
item.name = item.company_name;
});
Add the value that need to be shown in the display parameter:
$(".comp-search").typeahead({
hint: true,
highlight: true,
minLength: 1,
displayKey: 'company_name',
}, {
source: engine.ttAdapter(),
templates: {
empty: ['<div class="list-group search-results-dropdown"><div class="list-group-item">Nothing found.</div></div>'],
header: ['<div class="list-group search-results-dropdown">'],
suggestion: function (data) {
return '' + data.company_name + '';
}
},
display: function(data) { return data.company_name; }
});
Hope this is helpful

Specify id label for data loaded from array

Is it possible to load fully custom set of data into select2? I mean I can customize the option text property, can I also do it for id?
The code below works perfect
var dummy = [
{ id: 1, Name: "opt1" },
{ id: 2, Name: "opt2" }
];
$("#myselect").select2({
data: { results: dummy, text: "Name" },
formatSelection: function (item) { return item.Name; },
formatResult: function (item) { return item.Name }
});
However, my data incoming has the id property in caps. It surely would be possible for me to rename these objects' properties iterating through the received data set, yet the amount of data is pretty large and I certainly do not want to slow this simple load down. I'd also love to have these object properties stay.
Long story short, my data is like
var dummy = [
{ ID: 1, Name: "opt1" },
{ ID: 2, Name: "opt2" }
];
Is it possible to define an alternate id key?
yes, there is an option called id
Function used to get the id from the choice object or a string
representing the key under which the id is stored.
$("#myselect").select2({
id: 'ID',
data: { results: dummy, text: "Name" },
formatSelection: function (item) { return item.Name; },
formatResult: function (item) { return item.Name }
});

Categories