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;
}
});
});
})
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;
}
});
});
I don't know if it is possible to call a typescript inside a jquery function. if it is possible, what is the right method to do it?
this my component.ts
getCalendar(){
calendarOptions:Object = {
height: 'parent',
fixedWeekCount : false,
defaultDate: '2017-03-01',
editable: true,
eventLimit: true, // allow "more" link when too many
dayclick function
dayClick: function(date, jsEvent, view) {
this.addModal(); **this function is not working**
//console.log(jsEvent);
// alert('Clicked on: ' + date.format());
// alert('Coordinates: ' + jsEvent.pageX + ',' + jsEvent.pageY);
// alert('Current view: ' + view.name);
},
success: function(doc) {
var events = [];
$(doc).find('event').each(function() {
events.push({
title: $(this).attr('title'),
start: $(this).attr('start') // will be parsed
});
});
},
eventAllow: function(dropLocation, draggedEvent) {
if (draggedEvent.id === '999') {
return dropLocation.isAfter('2017-03-22'); // a boolean
}
else {
return true;
}
}
};
ngOnInit() {
this.getTotalEmployee();
this.getComputeAbsent();
this.getTotalAttendance();
// this.showEvent();
this.calendarOptions['events'] = this.events;
}
public catchError(error: any) {
let response_body = error._body;
let response_status = error.status;
if( response_status == 500 ){
this.error_title = 'Error 500';
this.error_message = 'The given data failed to pass validation.';
} else if( response_status == 200 ) {
this.error_title = '';
this.error_message = '';
}
}
showEvent(){
this._event_service.getEventList()
.subscribe(
data => {
this.events = Array.from(data);
this.calendarOptions['events'] = this.events;
console.log(this.calendarOptions['events']);
},
err => this.catchError(err)
);
}
getEvents() {
this._event_service.getEvents().subscribe(
data => {
this.eventsList = Array.from(data);
this.calendarOptions['events'] = this.eventsList;
},
err =>{}
);
}
this is my modal function that im trying to call in jquery function above
addModal() {
let disposable = this.modalService.addDialog(EventComponent, {
title:'Add Event'
}).subscribe((isConfirmed)=>{
});
}
getTotalAttendance() {
let pre;
this._attend_service.getTotalPresent().subscribe(
data => {
pre = Array.from(data);
this.present = pre[0].total_present;
},
err =>{}
);
}
getTotalEmployee() {
let totalEmp;
let filter = "Active";
this._attend_service.getTotalEmp(filter).subscribe(
data => {
totalEmp = data; // fetced record
this.total_emp = totalEmp[0].total_employee;
},
err =>{}
);
}
getComputeAbsent(){
let employee = parseInt(this.employee);
let attendance = parseInt(this.attendance);
this.totalAbsent = employee - attendance;
}
If you don't need the enclosed this
You can use the arrow function:
dayClick: (date, jsEvent, view)=> {
this.addModal();
}
Or you can store the outer this in a variable and use it later
var self = this; // store here
dayClick: function(date, jsEvent, view) {
self.addModal(); // use here
}
Edit:
getCalendar(){
var self = this; // ******
calendarOptions:Object = {
height: 'parent',
fixedWeekCount : false,
defaultDate: '2017-03-01',
editable: true,
eventLimit: true, // allow "more" link when too many
dayClick: function(date, jsEvent, view) {
self.addModal(); // *********
},
success: function(doc) {
var events = [];
$(doc).find('event').each(function() {
events.push({
title: $(this).attr('title'),
start: $(this).attr('start') // will be parsed
});
});
},
eventAllow: function(dropLocation, draggedEvent) {
if (draggedEvent.id === '999') {
return dropLocation.isAfter('2017-03-22'); // a boolean
}
else {
return true;
}
}
};
I am having a problem with autocomplete which is keep giving this error and I am unable to figure it out what is causing this issue. I have tried everything on so and still not able to figure it out.
I have bootstrap js and jquery ui also loaded in order of
jquery-1.11.2',
'jquery-ui',
'bootstrap'
$("#search-destination").autocomplete({
minLength: 3,
source: function (request, response) {
var url = "http://localhost/abc/public_html/query/visiting?";
var data = {term: $("#search-destination").val()};
$.getJSON(url, data, function (data) {
// data is an array of objects and must be transformed for autocomplete to use
var array = data.error ? [] : $.map(data.places, function (m) {
var data = {
label: (!isEmpty(m.city)) ? m.title + " (" + m.city + ")" : m.title,
};
return data;
});
response(array);
});
},
focus: function (event, ui) {
},
select: function (event, ui) {
}
}).data("ui-autocomplete")._renderItem = function (ul, item) {
var $a = $("<a></a>").text(item.label);
highlightText(this.term, $a);
return $("<li></li>").append($a).appendTo(ul);
};
I'm using select2 JQuery plugin to implement an autocomplete input-like element backed by a json endpoint, in this select2 input, I want the user can also create new objects into the same element by just giving them names or using the available on the endpoint.
My problem is that I need to access the processed 'data' mapping generated on 'processResults' key outside its function, actually inside the createTag function, I'm not sure if I need to use some JQuery method to access the result or control it using some global variable.
Here is my code:
HTML:
<p>
<select class="js-example-tags form-control" multiple="multiple"></select>
</p>
JS:
var $tags = $(".js-example-tags");
var responsejson;
$.fn.select2.amd.require(['select2/compat/matcher'], function(oldMatcher) {
$tags.select2({
ajax: {
data: function(params) {
var unicode = "\uf8ff";
var startAt = '"' + params.term + '"';
var endAt = '"' + params.term + unicode + '"';
var query = {
orderBy: "\"lowerCaseName\"",
startAt: startAt.toLowerCase(),
endAt: endAt.toLowerCase(),
print: "\"pretty\""
};
// Query paramters will be ?search=[term]&page=[page]
return query;
},
url: 'https://someappname.firebaseio.com/substancies.json?',
processResults: function(data, key) {
return {
results: $.map(data, function(obj, key) {
responsejson = {
id: key,
lower: obj.lowerCaseName,
text: obj.commonName
};
return responsejson;
})
};
}
},
tags: true,
createTag: function(params) {
if (responsejson !== undefined) {
console.log("Oppa");
}
var term = $.trim(params.term);
if (term === "") {
return null;
}
var optionsMatch = false;
var arrValue = $(".js-example-tags").select2('data');
for (var i = 0; i < arrValue.length; i++) {
var var1 = arrValue[i].lower;
var var2 = term.toLowerCase();
if (term.toLowerCase() === arrValue[i].lower) {
optionsMatch = true;
break;
}
}
if (optionsMatch) {
return null;
}
return {
id: -1,
text: term
};
},
minimumInputLength: 3,
tokenSeparators: [','],
casesensitive: false
});
});
I'm starting to learn and azure phonejs.
Todo list get through a standard example:
$(function() {
var client = new WindowsAzure.MobileServiceClient('https://zaburrito.azure-mobile.net/', 'key');
var todoItemTable = client.getTable('todoitem');
// Read current data and rebuild UI.
// If you plan to generate complex UIs like this, consider using a JavaScript templating library.
function refreshTodoItems() {
var query = todoItemTable.where({ complete: false });
query.read().then(function(todoItems) {
var listItems = $.map(todoItems, function(item) {
return $('<li>')
.attr('data-todoitem-id', item.id)
.append($('<button class="item-delete">Delete</button>'))
.append($('<input type="checkbox" class="item-complete">').prop('checked', item.complete))
.append($('<div>').append($('<input class="item-text">').val(item.text)));
});
$('#todo-items').empty().append(listItems).toggle(listItems.length > 0);
$('#summary').html('<strong>' + todoItems.length + '</strong> item(s)');
}, handleError);
}
function handleError(error) {
var text = error + (error.request ? ' - ' + error.request.status : '');
$('#errorlog').append($('<li>').text(text));
}
function getTodoItemId(formElement) {
return $(formElement).closest('li').attr('data-todoitem-id');
}
// Handle insert
$('#add-item').submit(function(evt) {
var textbox = $('#new-item-text'),
itemText = textbox.val();
if (itemText !== '') {
todoItemTable.insert({ text: itemText, complete: false }).then(refreshTodoItems, handleError);
}
textbox.val('').focus();
evt.preventDefault();
});
// Handle update
$(document.body).on('change', '.item-text', function() {
var newText = $(this).val();
todoItemTable.update({ id: getTodoItemId(this), text: newText }).then(null, handleError);
});
$(document.body).on('change', '.item-complete', function() {
var isComplete = $(this).prop('checked');
todoItemTable.update({ id: getTodoItemId(this), complete: isComplete }).then(refreshTodoItems, handleError);
});
// Handle delete
$(document.body).on('click', '.item-delete', function () {
todoItemTable.del({ id: getTodoItemId(this) }).then(refreshTodoItems, handleError);
});
// On initial load, start by fetching the current data
refreshTodoItems();
});
and it works!
Changed for the use of phonejs and the program stops working, even mistakes does not issue!
This my View:
<div data-options="dxView : { name: 'home', title: 'Home' } " >
<div class="home-view" data-options="dxContent : { targetPlaceholder: 'content' } " >
<button data-bind="click: incrementClickCounter">Click me</button>
<span data-bind="text: listData"></span>
<div data-bind="dxList:{
dataSource: listData,
itemTemplate:'toDoItemTemplate'}">
<div data-options="dxTemplate:{ name:'toDoItemTemplate' }">
<div style="float:left; width:100%;">
<h1 data-bind="text: name"></h1>
</div>
</div>
</div>
</div>
This my ViewModel:
Application1.home = function (params) {
var client = new WindowsAzure.MobileServiceClient('https://zaburrito.azure-mobile.net/', 'key');
var todoItemTable = client.getTable('todoitem');
var toDoArray = ko.observableArray([
{ name: "111", type: "111" },
{ name: "222", type: "222" }]);
var query = todoItemTable.where({ complete: false });
query.read().then(function (todoItems) {
for (var i = 0; i < todoItems.length; i++) {
toDoArray.push({ name: todoItems[i].text, type: "NEW!" });
}
});
var viewModel = {
listData: toDoArray,
incrementClickCounter: function () {
todoItemTable = client.getTable('todoitem');
toDoArray.push({ name: "Zippy", type: "Unknown" });
}
};
return viewModel;
};
I can easily add items to the list of programs, but from the server list does not come:-(
I am driven to exhaustion and can not solve the problem for 3 days, which is critical for me!
Specify where my mistake! Thank U!
I suggest you use a DevExpress.data.DataSource and a DevExpress.data.CustomStore instead of ko.observableArray.
Application1.home = function (params) {
var client = new WindowsAzure.MobileServiceClient('https://zaburrito.azure-mobile.net/', 'key');
var todoItemTable = client.getTable('todoitem');
var toDoArray = [];
var store = new DevExpress.data.CustomStore({
load: function(loadOptions) {
var d = $.Deferred();
if(toDoArray.length) {
d.resolve(toDoArray);
} else {
todoItemTable
.where({ complete: false })
.read()
.then(function(todoItems) {
for (var i = 0; i < todoItems.length; i++) {
toDoArray.push({ name: todoItems[i].text, type: "NEW!" });
}
d.resolve(toDoArray);
});
}
return d.promise();
},
insert: function(values) {
return toDoArray.push(values) - 1;
},
remove: function(key) {
if (!(key in toDoArray))
throw Error("Unknown key");
toDoArray.splice(key, 1);
},
update: function(key, values) {
if (!(key in toDoArray))
throw Error("Unknown key");
toDoArray[key] = $.extend(true, toDoArray[key], values);
}
});
var source = new DevExpress.data.DataSource(store);
// older version
store.modified.add(function() { source.load(); });
// starting from 14.2:
// store.on("modified", function() { source.load(); });
var viewModel = {
listData: source,
incrementClickCounter: function () {
store.insert({ name: "Zippy", type: "Unknown" });
}
};
return viewModel;
}
You can read more about it here and here.