Reputation: 129
I'm using jquery-ui to add autocomplete to an input field. I have essentially a two stage autocomplete that I'm trying to set up. Typing M will display an autocomplete of all options, selecting an option will enter that into the input EX: "machineName=", depending upon the first option selected, I then want to load a second autocomplete in the same field to show the values for that filter.
It works using static data, however the second autocomplete, is using API data so I have an AJAX call at the beginning of my script with a .then to chain together the creation of the autocomplete after the API has been hit, typing anything after machineName= results in nothing being displayed, however logging the value of the array I can see all the values in it.
var occupations = [{
value: "machineName=",
label: "machineName"
}, {
value: "ipAddress=",
label: "ipAddress"
},];
let machineNameAC = []
let switchTerm= [];
$.ajax({
url: '/api/data',
contentType: 'application/json',
dataType: 'json',
type: 'GET',
success: function(response){
console.log(response)
response.result.forEach((res) => {
machineNameAC.push(res.machineName)
console.log(machineNameAC)
})
}
}).then(() => {
$(function() {
function split(val) {
return val.split('=');
}
function extractLast(term) {
return split(term).pop();
}
$("#occupation").on("keydown", function(event) {
if (event.keyCode === $.ui.keyCode.TAB &&
$(this).autocomplete("instance").menu.active) {
event.preventDefault();
}
}).autocomplete({
minLength: 0,
source: function(request, response) {
var term = extractLast(request.term);
var results = [];
if (request.term.indexOf("=") > 0) {
var regE = /([^=]*)$/
if (request.term.endsWith('=')) {
console.log('term ',request.term)
switch (request.term){
case 'machineName=':
machineNameAC.forEach((machine)=>{
switchTerm.push(machine)
})
break;
}
}
console.log(switchTerm)
console.log(request.term)
if (parseInt(term) > 0) {
$.each(machineNameAC, function(k, v) {
console.log(k, v)
results.push(term + "" + v);
});
}
} else {
results = $.ui.autocomplete.filter(
occupations, request.term);
}
response(results);
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function(event, ui) {
var terms = split(this.value);
terms[0] = terms[0] + "=";
// 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;
}
});
});
})
Upvotes: 0
Views: 196
Reputation: 30899
Based on your code, your first stage determines if the user will look up a machine name versus an IP Address. Example:
machineName=descartes
or ipAddress=192.168.1.112
The example data you provided was:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
This data does not have a good relationship with the search in your initial code. I am assuming you have multiple items and you want to review the term
against the title
, yet this is not seen in your example. This is likely just sample data and does not represent your actual data. I must assume your data is more like:
[{
userId: 1001,
userName: "John Smith",
machineName: "jsmith-1",
ipAddress: "192.168.1.112",
siteLocation: "Shipping Bay 1"
}, {
userId: 1002,
userName: "Bettie Page",
machineName: "bpage-1",
ipAddress: "192.168.1.169",
siteLocation: "Champagne Room"
}];
If this is the case, you can forgo the prefix and make it a single state lookup that examines the term and identifies a machine name (words) versus an IP address (numbers and dots) and supplies the correct data. It's your choice to have it be two stage or single stage. You will need a filter function for both types, regardless.
Something like this:
function filterMachine(term, myData) {
var results = [];
$.each(myData, function(key, val) {
if (val.machineName.toLowerCase().indexOf(term.toLowerCase()) != -1) {
results.push($.extend(val, {
label: val.machineName,
value: val.machineName
});
}
});
return results;
}
function filterIp(term, myData) {
var results = [];
$.each(myData, function(key, val) {
if (val.ipAddress.indexOf(term) == 0) {
results.push($.eaxtend(val, {
label: val.ipAddress,
value: val.ipAddress
}));
}
});
return results;
}
It's not clear from your post what you are then doing with this data once a user Selects the item. Maybe filtering a table or updating a form. I do suggest using minLength
option, something like 2
. In this example, they might enter 192
or des
and this should be enough to logically determin what they are seeking.
Single Stage Example: https://jsfiddle.net/Twisty/60a1eLwz/32/
Your code will be a bit different as I am using the Echo feature of Fiddle, it does not use GET. Instead you POST data and it echos it back to the request.
JavaScript
$(() => {
var sampleData = [{
userId: 1001,
userName: "John Smith",
machineName: "jsmith-1",
ipAddress: "192.168.1.112",
siteLocation: "Shipping Bay 1"
}, {
userId: 1002,
userName: "Bettie Page",
machineName: "bpage-1",
ipAddress: "192.168.1.169",
siteLocation: "Champagne Room"
}];
function filterMachine(term, myData) {
var results = [];
$.each(myData, function(key, val) {
if (val.machineName.toLowerCase().indexOf(term.toLowerCase()) != -1) {
results.push($.extend(val, {
label: val.machineName,
value: val.machineName
}));
}
});
return results;
}
var apiData;
function filterIp(term, myData) {
var results = [];
$.each(myData, function(key, val) {
if (val.ipAddress.indexOf(term) == 0) {
results.push($.extend(val, {
label: val.ipAddress,
value: val.ipAddress
}));
}
});
return results;
}
$.ajax({
url: '/echo/json',
dataType: 'json',
type: 'POST',
data: {
json: JSON.stringify(sampleData)
},
success: function(response) {
apiData = response;
console.log('API Data ', apiData);
$("#inputField").on("keydown", function(event) {
if (event.keyCode === $.ui.keyCode.TAB &&
$(this).autocomplete("instance").menu.active) {
event.preventDefault();
}
}).autocomplete({
minLength: 2,
source: function(request, response) {
if (isNaN(request.term.replace(".", ""))) {
response(filterMachine(request.term, apiData));
} else {
response(filterIp(request.term, apiData));
}
},
focus: function() {
return false;
},
select: function(event, ui) {
$("#results").empty();
$.each(ui.item, function(key, value) {
$("#results").append(key + ": " + value + "<br />");
})
return false;
}
});
}
});
});
This is more similar to your code, where it collects all the data up front and it's in a variable. You could also just call the JSON content in the source
upon each request. The only benefit is if there are frequent changes to the data, you will catch more of them. If you pull all the data when the page loads, and the user sits on the page for 1 or 2 minutes, the data source could potentially be updated and the User will not get that new data.
In my opinion, this is a more friendly user interface, the User enters what they are looking for without having to search twice. They get suggests and pull up the result. With the Two Stage, they have to make an initial selection and then search. If you still want two stage, I suspect you can see where you would inject your prefix code again and then on the second stage pass the term to the correct function and append the specific detail you need as the result.
Two Stage Example: https://jsfiddle.net/Twisty/60a1eLwz/46/
JavaScript
$(() => {
var sampleData = [{
userId: 1001,
userName: "John Smith",
machineName: "jsmith-1",
ipAddress: "192.168.1.112",
siteLocation: "Shipping Bay 1"
}, {
userId: 1002,
userName: "Bettie Page",
machineName: "bpage-1",
ipAddress: "192.168.1.169",
siteLocation: "Champagne Room"
}];
function filterMachine(term, myData) {
var results = [];
$.each(myData, function(key, val) {
if (val.machineName.toLowerCase().indexOf(term.toLowerCase()) != -1) {
results.push($.extend(val, {
label: val.machineName,
value: val.machineName
}));
}
});
return results;
}
function filterIp(term, myData) {
var results = [];
$.each(myData, function(key, val) {
if (val.ipAddress.indexOf(term) == 0) {
results.push($.extend(val, {
label: val.ipAddress,
value: val.ipAddress
}));
}
});
return results;
}
$("#inputField").on("keydown", function(event) {
if (event.keyCode === $.ui.keyCode.TAB &&
$(this).autocomplete("instance").menu.active) {
event.preventDefault();
}
}).autocomplete({
minLength: 0,
source: function(request, response) {
if (request.term.length <= 2) {
response([{
label: "machineName",
value: "machineName="
}, {
label: "ipAddress",
value: "ipAddress="
}]);
} else {
var terms = request.term.split("=");
console.log(request.term, terms);
$.ajax({
url: '/echo/json',
dataType: 'json',
type: 'POST',
data: {
json: JSON.stringify(sampleData)
},
success: function(data) {
if (terms[0] == "machineName") {
response(filterMachine(terms[1], data));
} else {
response(filterIp(terms[1], data));
}
}
});
}
},
focus: function() {
return false;
},
select: function(event, ui) {
var terms = this.value.split("=");
terms[0] = terms[0] + "=";
// 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;
}
});
});
Upvotes: 1