jQuery UI Autocomplete - Building custom source - json

I have json array of the form:
[{"label":<some-label>,"spellings":[<list of spellings>]}, ...]
I need to parse the above array using jquery ui autocomplete. However, there are few constraints:
The autocomplete suggestions should involve matches from "spellings" but should suggest corresponding "label" only. e.g. if there are n "spellings" for a "label" then the autocomplete should show only that particular "label" for n "spellings".
On selecting from the suggestions provided, the corresponding "label" should only be reflected in the text input box.
How should I proceed with it? Any pointers?
And, how to iterate over list of "spellings" for a corresponding "label"?
This is what I'm trying to do, but giving garbled output.
var labels = []
var values = []
$.getJSON($url, function(data) {
$.each(data, function(key, val) {
for (var v in val.value)
values.push(val.value[v])
labels.push(val.label)
});
$("#text1").autocomplete({
minLength: 2,
source: values,
focus: function(event, ui) {
$("#text1").val(ui.item.label);
return false;
},
select: function(event, ui) {
$("#text1").val(ui.item.label);
return false;
}
});
});

I would build up a single source array of items, one for each spelling, where the label property is the label for each spelling and the value property is the spelling itself. This will enable you to quickly filter down results without having to iterate over each object's spelling array and check for matches which could take awhile.
Then, inside a function you define for source, you can do your own filtering logic, only allowing one instance of each "label" in the suggestions list.
Something like this should work (note that the autocomplete is initialized inside of the $.getJSON callback. This is necessary to make sure the source data is loaded before the widget is initialized):
$.getJSON($url, function(data) {
$.each(data, function (i, el) {
source.push({ label: el.label, value: el.label });
$.each(el.spellings, function (j, spelling) {
source.push({ label: el.label, value: spelling });
});
});
/* initialize the autocomplete widget: */
$("input").autocomplete({
source: function (request, response) {
var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i")
, results = [];
/* Make sure each entry is only in the suggestions list once: */
$.each(source, function (i, value) {
if (matcher.test(value.value) && $.inArray(value.label, results) < 0) {
results.push(value.label);
}
});
response(results);
}
});
});
Example: http://jsfiddle.net/MaMZt/

Related

Selected item should not shown in auto complete list

I am using auto-complete web service sing JSON, If i am selecting a list item that must not be appear again in auto-complete list;
JSON AJAX code:
select: function (event, ui) {
var terms = split(this.value);
if (terms.length <= 10) {
// remove the current input
terms.pop();
// add the selected item
terms.push(ui.item.value);
// add placeholder to get the comma-and-space at the end
terms.push("");
this.value = terms.join(", ");
return false;
}
else {
var last = terms.pop();
$(this).val(this.value.substr(0, this.value.length - last.length - 0)); // removes text from input
$(this).effect("highlight", {}, 1000);
$(this).addClass("red");
$("#warnings").html("<span style='color:red;'>Max skill reached</span>");
return false;
}
}
I am attaching screenshot also, please see here :
Like #Bindred mentioned in the comments to your question, an easier solution would be to use the Select2 jQuery library. It is not exactly what you are looking for, but as far as UX goes I think it would achieve a similar goal, and it is a breeze to get working.
I have added an example for you to use: https://jsfiddle.net/9cqc5876/9/
HTML
<select id="txtExpertise" multiple="multiple"></select>
JavaSript
$(document).ready(function() {
$("#txtExpertise").prop("disabled", "disabled");
// do your ajax request for data
//$.getJSON("../WebServices/WebServiceSkills.asmx/GetAutoCompleteData", function(data) {
// fake json data
var data = {"languages": ["Java", "C", "C++", "PHP", "Visual Basic",
"Python", "C#", "JavaScript", "Perl", "Ruby"]};
// populate the select
$.each(data.languages, function(key, val) {
$('#txtExpertise')
.append($("<option></option>")
.attr("value", key)
.text(val));
});
// activate the select2
$("#txtExpertise").select2();
$("#txtExpertise").prop("disabled", false);
//});
});

TVML listItemLockup click event

I'm using the 'Compilation.xml' template from the TVMLCatalog
I'd like to add a button click event to a 'listItemLockup'
<listItemLockup>
<ordinal minLength="2" class="ordinalLayout">0</ordinal>
<title>Intro</title>
<subtitle>00</subtitle>
<decorationLabel>(3:42)</decorationLabel>
</listItemLockup>
I've tried adding:
App.onLaunch = function(options) {
var templateURL = 'http://localhost:8000/hello.tvml';
var doc = getDocument(templateURL);
//doc.addEventListener("select", function() { alert("CLICK!") }, false);
var listItemLockupElement = doc.getElementsByTagName("listItemLockup");
listItemLockupElement.addEventListener("select", function() { alert("CLICK!") }, false);
}
addEventListener
void addEventListener (in String type, in Object listener, in optional Object extraInfo)
Is "select" the correct type?
I've been using the following tutorials
http://jamesonquave.com/blog/developing-tvos-apps-for-apple-tv-with-swift/
http://jamesonquave.com/blog/developing-tvos-apps-for-apple-tv-part-2/
Update
I'm getting an error
ITML <Error>: doc.getElementsByTagName is not a function. (In 'doc.getElementsByTagName("listItemLockup")', 'doc.getElementsByTagName' is undefined) - http://localhost:8000/main.js - line:27:58
I tried adding this to the 'onLaunch'
var listItemLockupElements = doc.getElementsByTagName("listItemLockup");
for (var i = 0; i < listItemLockupElements.length; i++) {
//var ele = listItemLockupElements[i].firstChild.nodeValue;
listItemLockupElements[i].addEventListener("select", function() { alert("CLICK!") }, false);
}
I'll see about the error first
Cross Post: https://forums.developer.apple.com/thread/17859
More common example I have seen by Apple is to define a single overall listener like:
doc.addEventListener("select", Presenter.load.bind(Presenter));
In your xml, assign unique ids to elements, or give them ways to identify them.
For example, the beginning would be something like:
load: function(event) {
var self = this,
ele = event.target,
attr_id = ele.getAttribute("id"),
audioURL = ele.getAttribute("audioURL"),
videoURL = ele.getAttribute("videoURL")
And then you can do whatever you want with your item.
if(audioURL && (event.type === "select" || event.type === "play")) {
//
}
My advice would be to study the Presenter.js file more carefully for this pattern.
Edit:
Answering your "Update" related to doc.getElementsByTagName is not a function. "doc" does not actually exist, but the general pattern is to get it with
var doc = getActiveDocument();
I assumed you would know the above.
Does that fix it?
var listItemLockupElement = doc.getElementsByTagName("listItemLockup”);
In this case, the listItemLockupElement is a NodeList, not an element. You can either iterate through the list and add an event listener to each listItemLockup, or you could add the event listener to the containing element.
When addressing items in a NodeList, you use the item(i) method rather than the standard array access notation:
listItemLockupElements.item(i).addEventListener("select", function() { })
See: https://developer.mozilla.org/en-US/docs/Web/API/NodeList/item
Adding event listeners is straightforward if you're using atvjs framework.
ATV.Page.create({
name: 'mypage',
template: your_template_function,
data: your_data,
events: {
select: 'onSelect',
},
// method invoked in the scope of the current object and
// 'this' will be bound to the object at runtime
// so you can easily access methods and properties and even modify them at runtime
onSelect: function(e) {
let element = e.target;
let elementType = element.nodeName.toLowerCase();
if (elementType === 'listitemlockup') {
this.doSomething();
}
},
doSomething: function() {
// some awesome action
}
});
ATV.Navigation.navigate('mypage');
Disclaimer: I am the creator and maintainer of atvjs and as of writing this answer, it is the only JavaScript framework available for Apple TV development using TVML and TVJS. Hence I could provide references only from this framework. The answer should not be mistaken as a biased opinion.

Change input value to a JSON value

I am looking to change the original text input value to a JSON value.
This randoms one of the similar artists that is searched. But I need that value to replace the input value whenever I hit the button to search.
So for example if I search Metallica, the code below would randomly pick 1 similar artist and then I want that to replace Metallica search originally set.
<input id="artists" type="text"</input>
<button id="searchy">Search Now</button>
function newartist(artist) {
var url = 'http://developer.echonest.com/api/v4/artist/similar';
var args = {
format:'json',
api_key : apikey,
name: artist,
results : 5,
};
$.getJSON(url, args,
function(data) {
var artist = data.response.artists[Math.floor(Math.random()*data.response.artists.length)];
console.log(artist.name);
}
);
}
function start() {
var artist = $.trim($("#artists").val());
}
$(document).ready(function() {
$("#searchy").click(start);
});
Is there a way to replace the original input value to my new JSON value?
Any help would be grateful, Thanks.
It sounds like you want to search on Metallica (for example), then randomly pick an artist from the results, and search again. I presume you don't want to keep looping.
If so, just call newartist from your success callback, with a flag saying not to do it again:
function newartist(artist, dontRepeat) {
var url = 'http://developer.echonest.com/api/v4/artist/similar';
var args = {
format:'json',
api_key : apikey,
name: artist,
results : 5,
};
$.getJSON(url, args,
function(data) {
var artist;
if (dontRepeat || data.response.artists.length === 0) {
// ...do something useful with the results
} else {
// Do the second search
artist = data.response.artists[Math.floor(Math.random()*data.response.artists.length)];
newartist(artist.name, true);
}
}
);
}

JSON object does not update correctly

First of all, I'm not sure if my title describes the problem correctly... I did search but didn't find anything that helped me out...
The project I'm working on has an #orderList. All orders have a delete option. After an order gets deleted the list is updated.
Sounds simple... I ran into a problem though.
/**
* Data returned at the end of selecting some options
*/
$.post(myUrl, $('#myForm').serialize(), function(data) {
// I build the orderlist
// The data returned is a JSON object holding session data (including orders)
buildOrderList(data);
...
// Do some other work
});
/*
* function to build the html list
*/
function buildOrderList(data) {
// Empty list
$('#orderList').empty();
// The click handler for the delete button is in here because it needs the data object
$(document).on('click', '[id^=delete_]', function() {
// Get the orderId from the delete button
var orderId = $(this).attr('id').split('_');
orderId = orderId['1'];
// I call the delete function
deleteOrder(orderId, data);
});
var html = '';
// Loop the data object
$.each(data, function(key,val){
...
// Put html code needed in var html
...
});
$('#orderList').append(html);
}
/*
* function to delete an order
*/
function deleteOrder(orderId, data) {
// Because of it depends on other 'products' in the list if the user can
// simply delete it, I use a jQuery dialog to give him some options.
// These options I send to a php script so it knows what should be deleted.
// This fires when a user clicks on the 'delete' button from a dialog.
// The dialog uses data to show options but does not change the value of data.
switch(data.type) {
case 'A':
delMsg += '<p>Some message for case A</p>';
delMsg += '<select>with some options for case A</select>';
$('#wizard_dialog').append(delMsg);
$('#wizard_dialog').dialog('option', 'buttons', [
{ text: "Delete", click: function() {
$.post(myUrl, $('#myDeleteOptions').serialize(), function(newData) {
// Now the returned data is the updated session data
// So I build the orderList again...
buildOrderList(newData);
...
// Do some other work
});
$( this ).dialog( "close" );
$(this).html(''); }},
{ text: "Cancel", click: function() { $( this ).dialog("close"); $(this).html(''); }}
] );
break;
case 'B':
// Do the same thing but different text and <select> elements
break;
}
}
The orderList updates correctly, however if I try to delete another order, the jQuery dialog gives me the option for the current (correct product) AND the option for the product that previously owned the id of the current. (Hope I didn't loose anyone in my attempt to explain the problem)
The main question is how to 'refresh' the data send to buildOrderList.
Since I call the function in a new $.post with fresh data object returned it should work, shouldn't it?
/**
* Enable the JQuery dialog
* (#wizard_dialog)
* this is the init (note that I only open the dialog in deleteOrder() and set text and buttons according to the data send to deleteOrder() )
*/
$('#wizard_dialog').dialog({
autoOpen: false,
resizable: false,
modal: true,
dialogClass: "no-close",
open: function() {
$('.ui-dialog-buttonpane').find('button:contains("Annuleren")').addClass('cancelButtonClass');
$('.ui-dialog-buttonpane').find('button:contains("Verwijderen")').addClass('deleteButtonClass');
$('.ui-dialog :button').blur(); // Because it is dangerous to put focus on 'OK' button
$('.ui-widget-overlay').css('position', 'fixed'); // Fixing overlay (else in wrong position?)
if ($(document).height() > $(window).height()) {
var scrollTop = ($('html').scrollTop()) ? $('html').scrollTop() : $('body').scrollTop(); // Works for Chrome, Firefox, IE...
$('html').addClass('noscroll').css('top',-scrollTop); // Prevent scroll without hiding the bar (thus preventing page to shift)
}
},
close: function() {
$('.ui-widget-overlay').css('position', 'absolute'); // Brake overlay again
var scrollTop = parseInt($('html').css('top'));
$('html').removeClass('noscroll'); // Allow scrolling again
$('html,body').scrollTop(-scrollTop);
$('#wizard_dialog').html('');
}
});
EDIT:
Because the problem could be in the dialog I added some code.
In the first code block I changed deleteOrder();
ANSWER
The solution was rather simple. I forgot to turn the click handler off before I added the new one. This returned the previous event and the new event.
$(document).off('click', '[id^=delete_]').on('click', '[id^=delete_]', function() {
// Get the orderId from the delete button
var orderId = $(this).attr('id').split('_');
orderId = orderId['1'];
// I call the delete function
deleteOrder(orderId, data);
});

JQuery UI catcomplete not returning selected result

I am using JQuery UI remote autocomplete with Categories.
$.widget( "custom.catcomplete", $.ui.autocomplete, {
_renderMenu: function( ul, items ) {
var self = this,
currentCategory = "";
$.each( items, function( index, item ) {
if ( item.category != currentCategory ) {
ul.append( "<li class='ui-autocomplete-category'>" + item.category + "</li>" );
currentCategory = item.category;
}
cat = currentCategory;
self._renderItem( ul, item );
});
}
});
$(function() {
$( "#birds" ).catcomplete({
delay:0,
source: "/search.html?term="+ $("#birds").val(),
minLength: 2,
select: function( event, ui ){
alert(ui.item.value);
}
});
});
The following is the result I get from the source:
[{"value":"Just a Product","id":"1","category":"Category Name"}]
The problem is that I can't get the alert(ui.item.value) to work and display the selected item.
Any help please?
Thanks.
This is not how you deal with the source.
see this.. http://api.jqueryui.com/autocomplete/#option-source
you can use
array
string
callback function
if you want to receive data from a page in json format, use jquery's $.ajax()
see this for ajax http://api.jquery.com/jQuery.ajax/
you can do something like
$.ajax({url: 'search.php?term='+$("#birds").val(),
dataType: 'json',
beforeSend: function(xhrObj){
xhrObj.setRequestHeader("Accept","application/json");
},
async: false,
success: function(data){
bArray=new Array;
bJson=data;
bArray.push(bJson[0].value);
bArray.push(bJson[0].id);
bArray.push(bJson[0].category);// for multiple records put these 3 lines in loop and replace 0 with a counter
}
});
$( "#birds" ).autocomplete({
source: bArray,
select:function(event,ui)
{alert(ui.item.value);}
});
dont forget to declare bArray, bJson
BUT i would still say you are doing it wrong. Instead of using value,id,category together you use use only one of them as autocomplete. can you tell what are you exactly showing in autocomplete