I have an application in AngularJS and I want to implement an autocomplete input for a certain list of programs.
My problem is that I have lots of programs in my database and I don't want to load them all when the page loads. Instead I load pages and have a button that loads the next page when clicked.
scope.loadPrograms = function() {
Programs.getPage($scope.page)
.success(function(data) {
$scope.allprograms.push.apply($scope.allprograms, data.campaigns);
$scope.page++;
if(data.pagination.pages < $scope.page) {
$scope.page = -1;
}
})
.error(function(data){
alert('There has been an error. Please try again later!');
});
}
and the button
<md-button ng-click="loadPrograms()" ng-show="page != -1">Load more data</md-button>
So this approach makes me do a request everytime I write/delete a letter in the autocomplete input, given the fact that I don't have all the program loaded on $scope. Is it ok to make so many request? Is there another approach?
Thanks.
EDIT
Ok so now I put a delay on the autocomplete, but the method doesn't work anymore.
// Search for programs
scope.querySearch = function(query) {
if (typeof pauseMonitor !== 'undefined') {
$timeout.cancel(pauseMonitor);
}
pauseMonitor = $timeout(function() {
var results = query ? scope.allprograms.filter(createFilterFor(query)) : [];
return results;
}, 250);
};
// Create filter function for a query string
function createFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(programs) {
return (programs.name.toLowerCase().indexOf(lowercaseQuery) != -1);
};
};
It enters in the createFilterFor method, finds a good match but doesn't show it anymore.
If you need to retrieve a set of words for the purpose of auto completion from a large database, one simple trick is to use $timeout with some time threshold which can detect the pauses of the user typing.
The idea is to prevent a request being generated for every key. You look for a pause in the user typing pattern and make your request there for the letters typed. This is a simple implementation of this idea in your key handler.
function processInput(input) {
if (typeof pauseMonitor !== 'undefined') {
$timeout.cancel(pauseMonitor);
pauseMonitor = $timeout(function() {
//make your request here
}, 250);
}
Take a look at ng-model-options
you can set a debounce time and some other interesting things.
ng-model-options="{ debounce: '1000' }"
Above line means the input value will be updated in the model after 1 sec
Related
I'm using web BLE. I have based my code according to the example of the heart rate measurement.
Everything is working fine most of the time. But sometimes, even if the connection is successfully made, when I try to bind to the notification, it doesn't work.
The link is made in this function :
_startNotifications(characteristicUuid) {
let characteristic = this._characteristics.get(characteristicUuid);
console.log(characteristic);
return characteristic.startNotifications().then(() => characteristic);
}
When everything is OK, I can see in the console that BluetoothRemoteGATTCharacteristic has a value : DataView(2) {}
Otherwise, when it's not working it has a value : null
I would like to be able to retry automatically, if I detect that the value is null. But I'm not familiar with Promise (I think this is it) and console.log(characteristic.value) doesn't work here.
How would you approach this ?
What I ended up doing is "bypass" the issue. So it's a more algorithmic resolution than a pure Javascript one.
I didn't change the connection function, so it is still called like this :
device._startNotifications(some_uuid).then(handleHeartRateMeasurement)
I check everything in the handleHeartRateMeasurement function :
var ready = false;
function handleHeartRateMeasurement(heartRateMeasurement) {
console.log("Hold on...");
heartRateMeasurement.addEventListener("characteristicvaluechanged", event => {
// Everytime the value change, this should be triggered
// If it did not, variable "ready" will stay false
ready = true;
var value = device.parseValue(event.target.value);
// Do something with value here
});
var check = function(){
// If we have received data from characteristic, we are ready to go !
if(ready === false){
console.log("Device connected but not receiving data");
// Stop the current notification subscription
device.stopNotificationsHeartRateMeasurement();
// Start a new one
device._startNotifications(some_uuid).then(handleHeartRateMeasurement);
setTimeout(check, 1000); // check again in a 1s
}
else{
console.log("Device connected and receiving data");
}
}
setTimeout(() => {
check();
}, 500);
}
I am new to Angular, but managed to make an Ajax-call and print out users from Random User Generator API in a list view.
Now I want to make a detailed view while clicked on a user.
In my HTML I make a function call: fetchInfoById(user.id.value)
In my script the function:
$scope.fetchInfoById = function(info_id) {
$http.get("https://randomuser.me/api/?id.value="+info_id)
//also tried: $http.get("https://randomuser.me/api/?id/value="+info_id)
.success(function(data) {
$scope.oneUserResult = data.results;
});
}
It does give me a user to a detail view, but not the chosen one. What am I doing wrong?
Thanks for your good suggestions.
I know it is a random generator, but setting parameters for the request to: "seed=...", the same persons is displayed on each listview request:
$http.get('https://randomuser.me/api/?results=15&seed=abc&inc=gender,name,location,email,dob,phone,cell,id,picture,info,nat&nat=gb')
.success(function(response){
$scope.userResult = response.results;
});
Then I fetched the id for each person and passed in as a parameter to the function call for the request for the detail view.
I tried with console.log() to make sure I passed in the right value for the detail view request and then even hardcoded the
parameter for the request ie:
$scope.getInfoById = function(info_id) {
console.log("from HTML: "+info_id.value ); // = JK 00 46 67
$http.get("https://randomuser.me/api/?id="+'JK 00 46 67 H') ...
The jason data behind the API is formatted like this for the id-property:
{
"results": [
{
"id": {
"name": "BSN",
"value": "04242023"
},...
I still haven't figured out how to get the one user by id. Getting different users all the time, even with hard coded id...
Instead of making the second request my solution was to a pass the "clicked user" as a parameter for the detailed view.
Change your code to this:
$scope.fetchInfoById = function(info_id) {
$http.get("https://randomuser.me/api/?id="+info_id)
//also tried: $http.get("https://randomuser.me/api/?id/value="+info_id)
.success(function(data) {
$scope.oneUserResult = data.results;
});
}
Also, make sure you are passing in the correct value to this function.
Fetch a list of users from API call "https://randomuser.me/api/?results=5".
$scope.getAllUsers= function(resultCount) {
$http.get("https://randomuser.me/api/?results="+resultCount)
.success(function(data) {
$scope.users= data.results;
});
Display them on the screen.
On click of one record fetch details for that particular record from users list fetched earlier.
$scope.getUserById= function(userId) {
return $scope.users.filter(function(user) {
return user.id.value=== userId;
})[0]; // apply necessary null / undefined checks wherever required.
}
another way using ng-model:
$scope.user = {};
$scope.fetchInfoById = function() {
$http.get("https://randomuser.me/api/?id="$scope.user.id)
.success(function(data) {
$scope.oneUserResult = data.results;
});
}
Ill post the code in a second. I am making a call to a service to pull data back. I get the data in an array and each one needs to make another call to another service. so I set my code up like so:
Services.getHelo({
assetSurfaceId: $scope.assetSurfaceId
}).then(function (resp) {
delete resp["$promise"];
delete resp["$resolved"];
$scope.entity.helo = resp;
for (var i = 0; i < $scope.entity.helo.length; i++) {
heloCall($scope.entity.helo, i);
initHelo($scope.entity.helo, i);
}
});
After I delete the promise and $resolved I start my for loop to call to my other functions. the initHelo function just builds a list for me and gets the vars ready for the next call into it. the heloCall makes the other calls i need.
Here is the helocall code:
var heloCall = function (r, i) {
jQuery("#helo").mask("Loading Surface Asset Helos...");
Services.getStatus({
toEntityId: r[i].assetHeloId,
toEntityTypeId: widget_consts.ASSET_HELICOPTER
}).then(function (s) {
delete s["$promise"];
delete s["$resolved"];
$scope.entity.helo[i].status = [];
$scope.entity.helo[i].status = s;
if (s[0].statusLkupShortDesc === "PMC" || s[0].statusLkupShortDesc === "NMC") {
Services.getReason({
toEntityId: s[0].statusId
}).then(function (reason) {
delete reason["$promise"];
delete reason["$resolved"];
if (reason) {
$scope.entity.helo[i].status.reason = [];
$scope.entity.helo[i].status.reason = reason;
initHelo($scope.entity.helo, i);
}
});
Services.getComment({
toEntityId: r[i].assetHeloId
}).then(function (info) {
delete info["$promise"];
delete info["$resolved"];
if (info) {
$scope.entity.helo[i].status.remark = {};
$scope.entity.helo[i].status.remark = info;
initHelo($scope.entity.helo, i);
}
});
}
initHelo($scope.entity.helo, i);
jQuery("#helo").unmask();
});
};
I do a check after the status call is made to make sure it is a status that Would have reasons with it and if it is i make the calls i need.
My issue is this only works when it wants to. I am not sure what i screwed up. I have been trying to get it to work right all day.
It has worked many times for me but I need it to work all the time.
Is there a better way to do this?
I actually have another call in this same file that is set up the same way and works each time with no issues
Any advice would be greatly appreciated
I figured it out. I needed to run a check to make sure that the length of the array returned from the service was not 0. it would cause issues depending on witch call was returned first. after I put this check it in runs fine.
I'm currently developing an AngularJS application which allows the user to activate their account, input user data and submit JSON data on completion.
What I'm trying to achieve:
If user data is valid (within the JSON data) redirect to '/form/' on submit else display invalid error message 'Invalid Account'.
Current Problems:
1. If the user enters the correct Membership and Activation number first the user is redirect to '/form/' but if the user enters the incorrect details then enters the correct details the redirect doesn't work.
2. I tried implementing an invalid alert to a if else statement but was alerted within 4/5 alerts at the same time, Unsure why this was happening?
I'm developing this project on Plnkr, please feel free to assist and point me in the right direct. Thank you.
http://plnkr.co/edit/5uPSn2ae0yFjYzujPFWm?p=preview
$scope.findMembership = function() {
angular.forEach($scope.membershipData.membershipNumber, function(value, key) {
if (key === $scope.membershipValue && value[0].activationNumber === $scope.activationValue) {
$location.path("/form/");
}
});
};
Update
formCtrl.controller('activation', function($scope, $location, $rootScope) {
var normalized = Object.keys($scope.membershipData.membershipNumber).map(function(k) {
return { key : k, val : $scope.membershipData.membershipNumber[k][0].activationNumber }
});
normalized = [
{"key":"541","val":"541X"},
{"key":"4692","val":"4692X"},
{"key":"45165","val":"45165X"},
{"key":"5464565","val":"5464565X"},
{"key":"54645651","val":"54645651X"},
{"key":"D4554160N","val":"D4554160NX"}
]
$scope.findMembership = function() {
if (normalized.some(function(o) {
return o.key == $scope.membershipValue && o.val == $scope.activationValue
})) $location.path("/form/")
}
});
Try changing your otherwise block to this:
otherwise({
templateUrl: 'view/activation.html',
controller: 'activation'
});
I think the redirect is causing the app to not be able to find the template it's looking for. Also I'd avoid using the $location.path() method inside the angular.forEach loop. Maybe normalize the data to a true array so you can use Array.some() instead.
Something like this:
var normalized = Object.keys($scope.membershipData.membershipNumber).map(function(k) {
return { key : k, val : $scope.membershipData.membershipNumber[k][0].activationNumber }
});
/*
normalized = [
{"key":"541","val":"541X"},
{"key":"4692","val":"4692X"},
{"key":"45165","val":"45165X"},
{"key":"5464565","val":"5464565X"},
{"key":"54645651","val":"54645651X"},
{"key":"D4554160N","val":"D4554160NX"}
]
*/
$scope.findMembership = function() {
if (normalized.some(function(o) {
return o.key == $scope.membershipValue && o.val == $scope.activationValue
})) $location.path("/form/")
}
I'm doing a project which uses javascript to get info from a view (written in Python and using the Django interface) based on the text a user enters in a field (querying on every keyup), and then display that info back. Basically, this either displays 'no job found' or displays the name, username, and balance for that job. In Firefox, this all works great. I can enter a JobID, it tells me the ID is new, and I can create the job. I can then immediately come back to the page and enter that ID, and my lookup returns the right info about the job.
The thing is, Internet Explorer 8 is being lazy. If I type a job ID in IE8, my functions calls the lookup page (/deposits/orglookup/?q=123) and gets a value. So if, for example, it gets False, I can then create a new job with that ID. If I then browse back and enter that same number in that same lookup field, Internet Explorer does not refresh the lookup page, so it returns false again. If I browse to that lookup page, I see that false value, but if I refresh it, I get the right information again. Any idea on how I can force this query every time I type in my lookup field, and not like IE refer to the cached page?
I will add that it does not do me much good to fix this on a per-user basis, as this is an organization-wide application, so I really could use a fix I can write into my code somewhere to force IE to actually refresh the lookup page every time it is supposed to.
Here's the code for the lookup function, if it helps. It is a bit messy, but I didn't write it so I'll try to include everything relevant:
$("#id_JobID").keyup(
function(event){
//only fire gets on 0-9, kp 0-9, backspace, and delete
if (event.keyCode in { 96:1, 97:1, 98:1, 99:1, 100:1, 101:1, 102:1, 103:1, 104:1, 105:1,
46:1,48:1, 49:1, 50:1, 51:1, 52:1, 53:1, 54:1, 55:1, 56:1, 57:1, 8:1})
{
if ($("#loadimg").attr("src") != "/static/icons/loading.gif") {
$("#loadimg").attr("src", "/static/icons/loading.gif");
}
if ($("#loadimg").length < 1) {
$("#id_JobID").parent().append("<img id=loadimg src=/static/icons/loading.gif>");
}
clearTimeouts(null); //clear all existing timeouts to stop any running lookups
GetCounter++;
currLoc = window.location.href.slice(window.location.href.indexOf('?') + 1).split('/').slice(-2,-1);
if (currLoc == 'restorebatch') {
var TimeoutId = setTimeout(function() {dynamicSearch('restorelookup');}, 400);
} else {
var TimeoutId = setTimeout(function() {dynamicSearch('orglookup');}, 400);
}
//alert(TimeoutID);
TimeoutBag[GetCounter] = {
'RequestNumber': GetCounter,
'TimeoutId': TimeoutId
}
}
}
);
function clearTimeouts(TimeoutBagKeys) //TimeoutBagKeys is an array that contains keys into the TimeoutBag of Timeout's you want to clear
{
if(TimeoutBagKeys == null) //if TimeoutBagKeys is null, clear all timeouts.
{
for (var i = 0; i < TimeoutBag.length; i++)
{
if (TimeoutBag[i] != null) {
clearTimeout(TimeoutBag[i].TimeoutId);
}
}
}
else //otherwise, an array of keys for the timeout bag has been passed in. clear those timeouts.
{
var ClearedIdsString = "";
for (var i = 0; i < TimeoutBagKeys.length; i++)
{
if (TimeoutBag[TimeoutBagKeys[i]] != null)
{
clearTimeout(TimeoutBag[TimeoutBagKeys[i]].TimeoutId);
ClearedIdsString += TimeoutBag[TimeoutBagKeys[i]].TimeoutId;
}
}
}
}
function dynamicSearch(viewname) {
$(".lookup_info").slideUp();
if ($("#id_JobID").val().length >= 3) {
var orgLookupUrl = "/deposits/" + viewname + "/?q=" + $("#id_JobID").val();
getBatchInfo(orgLookupUrl);
}
else if ($("#id_JobID").val().length == 0) {
$("#loadimg").attr("src", "/static/icons/blank.gif");
$(".lookup_info").slideUp();
}
else {
$("#loadimg").attr("src", "/static/icons/loading.gif");
$(".lookup_info").slideUp();
}
}
function getBatchInfo(orgLookupUrl) {
$.get(orgLookupUrl, function(data){
if (data == "False") {
$("#loadimg").attr("src", "/static/icons/red_x.png");
$(".lookup_info").html("No batch found - creating new batch.");
$("#lookup_submit").val("Create");
$(".lookup_info").slideDown();
toggleDepInputs("on");
}
else {
$("#loadimg").attr("src", "/static/icons/green_check.png");
$("#lookup_submit").val("Submit");
$(".lookup_info").html(data);
$(".lookup_info").slideDown()
toggleDepInputs("off");
};
});
}
There are three solutions to this:
Use $.post instead of $.get.
Add a random GET parameter to your URL, e.g. ?update=10202203930489 (of course, it needs to be different on every request).
Prohibit caching on server-side by sending the right headers (if-modified-since).
You need to make the URL unique for every request. The failproof way is to introduce new GET parameter which has a timestamp as its value - so the URL is unique with every request, since timestamp is always changing, so IE can't cache it.
url = "/deposits/orglookup/?q=123&t=" + new Date().getTime()
So instead of only one parameter (q) you now have two (q and t) but since servers usually don't care bout extra parameters then it's all right
One trick that often works is to append a timestamp to the lookup URL as a querystring parameter, thus generating a unique URL each time the request is made.
var orgLookupUrl = "/deposits/" +
viewname + "/?q=" +
$("#id_JobID").val() + "&time=" + new Date().getTime();;