Stopping a function until user presses enter jQuery - javascript

I've been working on this for days and I can't seem to find a solution.
I want this script to wait until the user presses the enter key after the first value has been inputted into the field. I want the script to keep doing this every time a value is added, but I can't quite seem to find out how to do this.
$(document).ready(function() {
console.log("script loaded");
var apiKey = "";
var itemImage = $(".title-wrap img");
var itemList = [];
var i = 0;
var addPage = false;
// Run through all images and grab all item ID's.
function scrapeItems() {
itemImage.each(function() {
var grabItemID = $(this).attr("src").match(/\d+/)[0];
var disabled = $(this).closest("li.clearfix").hasClass("disabled");
// Add item number as class for easy reference later.
$(this).addClass("item-" + grabItemID);
// If the item's row has "disabled" class, skip this item.
if (disabled) {
return true;
scrapeItems();
}
// Add item to array.
itemList.push(grabItemID);
});
}
scrapeItems();
// Call the API request function and start gathering all bazaar prices.
function getPricing() {
console.log("script started");
$.each(itemList, function(key, value) {
// Set three second timer per API request.
setTimeout(function() {
// Actual API request.
return $.ajax({
dataType: "json",
url: "https://api.torn.com/market/" + value,
data: {
selections: "bazaar",
key: apiKey
},
// When data is received, run this.
success: function(data) {
console.log(value + " request was successful");
var cheapest = null;
// Run through all results and return the cheapest.
$.each(data["bazaar"], function(key, val) {
var cost = val["cost"];
if (cheapest == null || cost < cheapest) {
cheapest = cost;
}
});
var inputMoney = $(".item-" + value).closest("li.clearfix").find(".input-money:text");
inputMoney.val(cheapest - 1).focus();
// I WANT THE FUNCTION TO WAIT HERE UNTIL THE USER PRESSES ENTER
},
// When data is not received, run this.
error: function() {
console.log(value + " request was NOT successful");
}
});
}, key * 3000);
});
}
function checkPage() {
var i = 0;
var url = window.location.href;
i++
setTimeout(function() {
if (url.indexOf("bazaar.php#/p=add") > 0) {
addPage = true;
addButton();
} else {
checkPage();
}
}, i * 1000);
}
checkPage();
function addButton() {
$("#inventory-container").prepend('<button id="start-button" style="margin-bottom:10px;margin-right:10px;">Run Auto-pricing script</button><p id="s-desc" style="display:inline-block;font-weight:bold;text-transform:uppercase;">Press the enter key after the price has shown up!</p>');
}
$(document).on("click", "#start-button", function() {
getPricing();
});
});
I'm at a complete loss on this one guys, so all help is appreciated!

I think you should break down your code a bit more, and move the "on enter" part of the code into a separate function instead of waiting for user input within that success callback.
e.g in pseudo code, different stages of the scraping
let priceData;
const preProcessPriceData = (data) => {
// do some pre-processing, validate, change data formats etc
// return processed data
};
const processPriceData = (data) => {
// called when price data is ready and user pressed enter
// in other words - script continues here
console.log(priceData, 'or', data);
};
scrapeItems();
// in get prices function - remove event handler
$("#some-input-user-is-pressing-enter-in").offOnEnter(processPriceData);
priceData = null;
getPrices().then((data) => {
priceData = data;
let processedData = preProcessPriceData(data);
// add listener to wait for user input
$("#some-input-user-is-pressing-enter-in").onEnter(() => {
// script continues after user presses enter
processPriceData(processedData);
});
});

Related

Change the cells of a table in an interval time using OData model

I have this code and I need my table to show the first 10 patients and, after 10 seconds, show the next 10 without touching any button (automatically).
I'm looking for something similar to this: https://embed.plnkr.co/ioh85m5OtPmcvPHyl3Bg/
But with an OData model (as specified on my view and controller).
This is my view:
<Table id="tablaPacientes" items="{/EspCoSet}">
<columns>
<!-- ... -->
</columns>
<ColumnListItem>
<ObjectIdentifier title="{Bett}" />
<!-- ... -->
</ColumnListItem>
</Table>
This is my controller:
onInit: function () {
var oModel = this.getOwnerComponent().getModel("zctv");
this.getView().setModel(oModel);
},
onBeforeRendering: function () { // method to get the local IP because I need it for the OData
var ipAddress;
var RTCPeerConnection = window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
var self = this;
function grepSDP (sdp) {
var ip = /(192\.168\.(0|\d{0,3})\.(0|\d{0,3}))/i;
sdp.split('\r\n').forEach(function (line) {
if (line.match(ip)) {
ipAddress = line.match(ip)[0];
self.setIp(ipAddress);
}
});
}
if (RTCPeerConnection) {
(function () {
var rtc = new RTCPeerConnection({
iceServers: []
});
rtc.createDataChannel('', {
reliable: false
});
rtc.onicecandidate = function (evt) {
if (evt.candidate) {
grepSDP(evt.candidate.candidate);
}
};
rtc.createOffer(function (offerDesc) {
rtc.setLocalDescription(offerDesc);
}, function (e) {
console.log("Failed to get Ip address");
});
})();
}
},
setIp: function (ip) {
this.getView().byId("planta").bindElement({
path: "/CenTVSet('" + ip + "')"
});
var oModel = this.getView().getModel();
var that = this;
oModel.read("/CenTVSet('" + ip + "')", {
success: function (oData, oRes) {
var einri = oData.Einri;
var orgpf = oData.Orgpf;
var oTable = that.getView().byId("tablaPacientes");
var oBinding = oTable.getBinding("items");
var aFilters = [];
var filterO = new Filter("Orgna", sap.ui.model.FilterOperator.EQ, orgpf);
aFilters.push(filterO);
var filterE = new Filter("Einri", sap.ui.model.FilterOperator.EQ, einri);
aFilters.push(filterE);
oBinding.filter(aFilters);
}
});
}
I searched some functions like IntervalTrigger but I really don't know how can I use it for this example.
Here are some small samples:
OData V4: https://embed.plnkr.co/4zIAH7q2E0lngbyX
OData V2: https://embed.plnkr.co/rNa0TktXiQqSCGJV
startList: function(listBase, $skip, $top, restInfo) {
let startIndex = $skip;
let length = $top;
let totalSize;
(function repeat(that) {
const bindingInfo = Object.assign({ startIndex, length }, restInfo);
listBase.bindItems(bindingInfo);
listBase.data("repeater", event => {
totalSize = event.getParameter("total"); // $count value
startIndex += $top;
startIndex = startIndex < totalSize ? startIndex : 0;
setTimeout(() => repeat(that), 2000);
}).attachEventOnce("updateFinished", listBase.data("repeater"), that);
})(this);
},
stopList: function(listBase) {
listBase.detachEvent("updateFinished", listBase.data("repeater"), this);
},
The samples make use of startIndex and length in the list binding info which translates to $skip and $top system queries of the entity request URL. I.e. appending those system queries to the request URL (e.g. https://<host>/<service>/<EntitySet>?$skip=3&$top=3), should return the correct set of entities like this.
Additional options for the list binding info can be found in the UI5 documentation as I explained here.
JavaScript part
The interval is implemented with an IIFE (Immediately Invoked Function Expression) in combination with setTimeout instead of setInterval.
setInterval has the following disadvantages:
The callback is not immediately invoked. You'd have to wait 10 seconds first to trigger the 1st callback.
Does not wait for the data response to arrive. This may cause skipping a batch or showing it for a too short period of time because the delay simply continues regardless of the server response.
setTimeout instead offers a better control when the next batch should be requested.
You could bind you items using bindItems, pass skip,top parameters and wrap the whole thing in a setInterval
var iSkip = 0;
var iTop = 10;
setInterval(function() {
table.bindItems("/EspCoSet", {
urlParameters: {
"$skip": iSkip.toString() // Get first 10 entries
"$top": iTop.toString()
},
success: fuction (oData) {
iSkip = iTop; // Update iSkip and iTop to get the next set
iTop+= 10;
}
...
}, 10000); // Each 10 seconds
)
Almost the same thing, just use oModel.read to read the entities into you viewModel.allEntities, bind your table to the viewModel.shownEntities and use a setInterval to get the next 10 from allEntities to update shownEntities.

Updating a specific record in the database?

I'm using Javascript with applab. The code creates new users fine, but it only updates the first record. Any ideas how to fix this so that it will update for each new and returning user? I think I need to use a math function with "mostRecentID" or "record.ID" to achieve this.
Link to Project: https://studio.code.org/projects/applab/bSkJw2auZx8Iq7ZGemsT7HwQrOVBWFPl6eLl0R0a6VU If you go to "how it works (view code)", and then you can see the code and the database.
//The database for this code already exists.
//This code creates new users fine, but it only updates the first record.
//What code will update a specific ID in the database
//I think I need to use a math function with "mostRecentID" or "record.ID" to achieve this.
var mostRecentID=1;
var player = {};
var UserID = getUserId();
onEvent("addNewRecordButton", "click", function() {
readRecords("AllUserData", player, function(records) {
var count = records.length;
if (count > 1) {
} else if ((count == 1)) {
} else {
var player = {};
player.UserID = getUserId();
player.Username=getText("usernameInput");
createRecord("AllUserData", player, function(record) {
mostRecentID=record.id;
});
}
});
});
onEvent("updateInformationButton", "click", function() {
updateRecord("AllUserData", {id:mostRecentID,UserID:getUserId(),Username:(getText("usernameInput"))}, function() {
});
});
Let's say you wanted to update a record matching the current user's user ID. You would need to specify the user ID in the parameters object.
readRecords("AllUserData", {UserID: getUserId()}, function (records) {
if (records.length > 0) {
updateRecord("AllUserData", {id: records[0].id, UserID:getUserId(),Username:(getText("usernameInput"))}, function () {
// callback code
});
} else {
createRecord("AllUserData", {UserID:getUserId(),Username:(getText("usernameInput"))}, function () {
// callback code
});
}
});

Improving performance refreshing items every second

I have an API that send updates via Server Sent Events (SSE) every seconds for my items.
Basically I have a collection $scope.items that contain a lot of information within and every second one item of this list is updated.
What I'm doing is:
var source;
if (!!window.EventSource) {
source = new EventSource('/updates');
} else {
alertify.error('SSE not supported');
}
// Emit SSE for items
source.addEventListener('items', function (e) {
var data = JSON.parse(e.data);
$timeout(function () {
var item_index = _.findIndex($scope.items, function (item) {
return item.id === data.id;
});
var status = data.status;
if (item_index > -1) {
if (status === 'cancelled') {
$scope.items.splice(item_index, 1);
}
$scope.items[item_index] = data;
$scope.$apply();
} else {
$scope.items.push(data);
}
});
}, false);
I was wondering if I'm doing it right or if I can improve this code because the app is quite slow when I start to have many and many items to cycle every second...
Looking at your code:
var item_index = _.findIndex($scope.items, function (item) {
return item.id === data.id;
});
I fear that a full search is done every time you access item_index
I would define a function:
function getIndex(data){
_.findIndex($scope.items, function (item) {
return item.id === data.id;
});
};
And the call it from within your
$timeout(function () {
var item_index = getIndex(data);
...

How to set callback order invocation same as target function invocation

I have a problem in my project.
To describe this issue I have wrote simplified code snippet:
function waitFor(fnReady, fnCallback) {
var check = function() {
if (fnReady()) {
fnCallback();
}
else {
setTimeout(check, 100); // wait another 100ms, and try again
}
};
check();
}
var result = 0;
var flag = true;
function ajaxRequest() {
setTimeout(
function() { flag = false;
console.log('ping');
},3000
);
}
function ajaxRequestHandler() {
setTimeout(
function() { flag = true;
console.log('pong');
}, 200
);
}
for(var i =0;i<10; i++){
waitFor(function() { return flag; }, ajaxRequest);
waitFor(function() { return !flag; }, ajaxRequestHandler);
}
it returns:
ping - 10 times
pong - 10 times
desired result:
ping
3 second timeout
ping
---------------------
ping
3 second timeout
pong
--------------------
.....
Can you help correct my code?
UPDATE
Actual problem:
I have a google map.
I have a lot of places when I should to redraw it.
For application logic very important that If I send
request1
request2
request3
request4
I should handle responses in the this order
handle response of request1
handle response of request2
handle response of request3
handle response of request4
Problem that I don't know order of requests.
In different places of file I see following code rows:
google.maps.event.addListener(searchBox, 'bounds_changed', renderTerminalsOnMapAndFitBounds);
...
$.getJSON('getAllTerminals.json', renderTerminalsOnMapAndFitBounds);
.....
$.getJSON('getAllTerminalsInsideRectangle.json', renderTerminalsOnMapAndFitBounds);
...
$.getJSON('getAllTerminalsInsideCircle.json', renderTerminalsOnMapAndFitBounds);
...
$.getJSON('getBigTerminals.json', renderTerminalsOnMapAndFitBounds);
........
renderTerminalsOnMapAndFitBounds method sends request to server and in succes alternative render result on map. But this event happens very often
Try this pattern
var map = "abcdefghi".split("");
var responses = []; // collect responses
$.ajaxSetup({
beforeSend : function(jqxhr, settings) {
jqxhr.id = Number(settings.data.split(/id=/)[1]); // add `id` to `request`
console.log(settings.data.split(/id=/)[1]);
}
});
var request = function(id, data) {
// append `id` to `id` data
return $.post("/echo/json/", {json:JSON.stringify([data]), id:id})
};
$.each(map, function(k, v) {
setTimeout(function() {
request(k + 1, v)
.done(function(data) {
// do stuff at each response
console.log(data); // note return values
})
.always(function(data, textStatus, jqxhr) {
// do stuff at each response
responses.push([jqxhr.id, data[0]]);
// do stuff when all requests completed , results items in `responses`
if (responses.length === map.length) {
responses.sort(); // sort `responses` based on `id`
// do stuff with `responses`
console.log(responses);
}
});
},1 + Math.random() * 1000) // async
});
jsfiddle http://jsfiddle.net/guest271314/g254bbjg/
my variant:
var index = 0;
// callback function
function tryMe (param1) {
waitFor(function(){return param1 == index},
function(){console.log(param1);
index++;
}
)
}
// callback executer
function callbackTester (callback,i) {
setTimeout( function(){callback(i);}, 20000 - i*1000);
}
// test function
for(var i=0 ; i<10 ; i++){
callbackTester ( tryMe,i );
}
function waitFor(fnReady, fnCallback) {
var check = function() {
if (fnReady()) {
fnCallback();
}
else {
setTimeout(check, 100); // wait another 100ms, and try again
}
};
check();
}
http://jsfiddle.net/x061dx75/17/
I personally would use promises for this, but you've said no promises (not sure why), so here's a generic sequencer algorithm in plain javascript (tested in the jsFiddle linked below):
function sequence(fn) {
// initialize sequence data upon first use
if (typeof sequence.low === "undefined") {
sequence.low = sequence.high = 0;
sequence.results = {};
}
// save id in local variable so we can reference it in the closure from the function below
var id = sequence.high;
// advance to next sequence number
++sequence.high;
// initialize the result value for this sequence callback
sequence.results[id] = {fn: fn, args: [], ready: false, context: null};
return function(/* args */) {
// save args and context and mark it ready
var args = Array.prototype.slice.call(arguments, 0);
// get the results object for this callback and save info in it
var thisResult = sequence.results[id];
thisResult.args = args;
thisResult.context = this;
thisResult.ready = true;
// now process any requests in order that are ready
for (var i = sequence.low; i < sequence.high; i++) {
var result = sequence.results[i];
// if this one is ready, process it
if (result.ready) {
// increment counter past this result
++sequence.low;
// remove this stored result
delete sequence.results[i];
// process this result
result.fn.apply(result.context, result.args);
} else {
// if this one not ready, then nothing to do yet
break;
}
}
};
}
// your usage:
google.maps.event.addListener(searchBox, 'bounds_changed', sequence(renderTerminalsOnMapAndFitBounds));
...
$.getJSON('getAllTerminals.json', sequence(renderTerminalsOnMapAndFitBounds));
.....
$.getJSON('getAllTerminalsInsideRectangle.json', sequence(renderTerminalsOnMapAndFitBounds));
...
$.getJSON('getAllTerminalsInsideCircle.json', sequence(renderTerminalsOnMapAndFitBounds));
...
$.getJSON('getBigTerminals.json', sequence(renderTerminalsOnMapAndFitBounds));
........
Working demo: http://jsfiddle.net/jfriend00/aqugm1fs/
Conceptually, what this does is as follows:
Pass a substitute completion handler in place of the normal completion callback.
This substitute function marks each response with a sequence id and saved the original completion handler.
If a response comes back while another response with a lower sequence id is still pending, then the result is just stored and saved for later.
As each response comes in, it processes as many responses in sequence as are ready
Note: while all the examples you have use the same callback function, this will work with any callback function so it would work with a mix of different types of operations.

Angularjs must refresh page to see changes

What I have is simple CRUD operation. Items are listed on page, when user clicks button add, modal pops up, user enters data, and data is saved and should automatically (without refresh)be added to the list on page.
Service:
getAllIncluding: function(controllerAction, including) {
var query = breeze.EntityQuery.from(controllerAction).expand(including);
return manager.executeQuery(query).fail(getFailed);
},
addExerciseAndCategories: function(data, initialValues) {
var addedExercise = manager.createEntity("Exercise", initialValues);
_.forEach(data, function(item) {
manager.createEntity("ExerciseAndCategory", { ExerciseId: addedExercise._backingStore.ExerciseId, CategoryId: item.CategoryId });
});
saveChanges().fail(addFailed);
function addFailed() {
removeItem(items, item);
}
},
Controller:
$scope.getAllExercisesAndCategories = function() {
adminCrudService.getAllIncluding("ExercisesAndCategories", "Exercise,ExerciseCategory")
.then(querySucceeded)
.fail(queryFailed);
};
function querySucceeded(data) {
$scope.queryItems = adminCrudService.querySucceeded(data);
var exerciseIds = _($scope.queryItems).pluck('ExerciseId').uniq().valueOf();
$scope.exerciseAndCategories = [];
var createItem = function (id, exercise) {
return {
ExerciseId: id,
Exercise : exercise,
ExerciseCategories: []
};
};
// cycle through ids
_.forEach(exerciseIds, function (id) {
// get all the queryItems that match
var temp = _.where($scope.queryItems, {
'ExerciseId': id
});
// go to the next if nothing was found.
if (!temp.length) return;
// create a new (clean) item
var newItem = createItem(temp[0].ExerciseId, temp[0].Exercise);
// loop through the queryItems that matched
_.forEach(temp, function (i) {
// if the category has not been added , add it.
if (_.indexOf(newItem.ExerciseCategories, i.ExerciseCategory) < 0) {
newItem.ExerciseCategories.push(i.ExerciseCategory);
}
});
// Add the item to the collection
$scope.items.push(newItem);
});
$scope.$apply();
}
Here is how I add new data from controller:
adminCrudService.addExerciseAndCategories($scope.selectedCategories, { Name: $scope.NewName, Description: $scope.NewDesc });
So my question is, why list isn't updated in real time (when I hit save I must refresh page).
EDIT
Here is my querySuceeded
querySucceeded: function (data) {
items = [];
data.results.forEach(function(item) {
items.push(item);
});
return items;
}
EDIT 2
I believe I've narrowed my problem !
So PW Kad lost two hours with me trying to help me to fix this thing (ad I thank him very very very much for that), but unfortunately with no success. We mostly tried to fix my service, so when I returned to my PC, I've again tried to fix it. I believe my service is fine. (I've made some changes as Kad suggested in his answer).
I believe problem is in controller, I've logged $scope.items, and when I add new item they don't change, after that I've logged $scope.queryItems, and I've noticed that they change after adding new item (without refresh ofc.). So probably problem will be solved by somehow $watching $scope.queryItems after loading initial data, but at the moment I'm not quite sure how to do this.
Alright, I am going to post an answer that should guide you on how to tackle your issue. The issue does not appear to be with Breeze, nor with Angular, but the manner in which you have married the two up. I say this because it is important to understand what you are doing in order to understand the debug process.
Creating an entity adds it to the cache with an entityState of isAdded - that is a true statement, don't think otherwise.
Now for your code...
You don't have to chain your query execution with a promise, but in your case you are returning the data to your controller, and then passing it right back into some function in your service, which wasn't listed in your question. I added a function to replicate what yours probably looks like.
getAllIncluding: function(controllerAction, including) {
var query = breeze.EntityQuery.from(controllerAction).expand(including);
return manager.executeQuery(query).then(querySucceeded).fail(getFailed);
function querySucceeded(data) {
return data.results;
}
},
Now in your controller simply handle the results -
$scope.getAllExercisesAndCategories = function() {
adminCrudService.getAllIncluding("ExercisesAndCategories", "Exercise,ExerciseCategory")
.then(querySucceeded)
.fail(queryFailed);
};
function querySucceeded(data) {
// Set your object directly to the data.results, because that is what we are returning from the service
$scope.queryItems = data;
$scope.exerciseAndCategories = [];
Last, let's add the properties we create the entity and see if that gives Angular a chance to bind up properly -
_.forEach(data, function(item) {
var e = manager.createEntity("ExerciseAndCategory");
e.Exercise = addedExercise; e.Category: item.Category;
});
So I've managed to solve my problem ! Not sure if this is right solution but it works now.
I've moved everything to my service, which now looks like this:
function addCategoriesToExercise(tempdata) {
var dataToReturn = [];
var exerciseIds = _(tempdata).pluck('ExerciseId').uniq().valueOf();
var createItem = function (id, exercise) {
return {
ExerciseId: id,
Exercise: exercise,
ExerciseCategories: []
};
};
// cycle through ids
_.forEach(exerciseIds, function (id) {
// get all the queryItems that match
var temp = _.where(tempdata, {
'ExerciseId': id
});
// go to the next if nothing was found.
if (!temp.length) return;
// create a new (clean) item
var newItem = createItem(temp[0].ExerciseId, temp[0].Exercise);
// loop through the queryItems that matched
_.forEach(temp, function (i) {
// if the category has not been added , add it.
if (_.indexOf(newItem.ExerciseCategories, i.ExerciseCategory) < 0) {
newItem.ExerciseCategories.push(i.ExerciseCategory);
}
});
// Add the item to the collection
dataToReturn.push(newItem);
});
return dataToReturn;
}
addExerciseAndCategories: function (data, initialValues) {
newItems = [];
var addedExercise = manager.createEntity("Exercise", initialValues);
_.forEach(data, function (item) {
var entity = manager.createEntity("ExerciseAndCategory", { ExerciseId: addedExercise._backingStore.ExerciseId, CategoryId: item.CategoryId });
items.push(entity);
newItems.push(entity);
});
saveChanges().fail(addFailed);
var itemsToAdd = addCategoriesToExercise(newItems);
_.forEach(itemsToAdd, function (item) {
exerciseAndCategories.push(item);
});
function addFailed() {
removeItem(items, item);
}
}
getAllExercisesAndCategories: function () {
var query = breeze.EntityQuery.from("ExercisesAndCategories").expand("Exercise,ExerciseCategory");
return manager.executeQuery(query).then(getSuceeded).fail(getFailed);
},
function getSuceeded(data) {
items = [];
data.results.forEach(function (item) {
items.push(item);
});
exerciseAndCategories = addCategoriesToExercise(items);
return exerciseAndCategories;
}
And in controller I have only this:
$scope.getAllExercisesAndCategories = function () {
adminExerciseService.getAllExercisesAndCategories()
.then(querySucceeded)
.fail(queryFailed);
};
function querySucceeded(data) {
$scope.items = data;
$scope.$apply();
}

Categories