I trigger the following function when a tooltip is clicked. It is an ajax poll.
There can be many tooltips on the page, and more than one can need access to the data retrieved from the server.
What I want to achieve is to have this poll running as one instance - so if the user clicks a different tooltip the polling stops, rather than being duplicated.
Would be grateful if you could help.
Thanks
function doConversationsAjaxLongPoll(tablename){
clientSubmit = new Object;
// HERE WE'RE GOING TO GET A LIST OF THE ROWIDS THAT WE NEED TO POLL FOR, MAKE AN OBJECT OUT OF THEM. DO THIS BY LOOKING AT WHICH //TOOLIPS HAVE CLASS OPEN
var tooltips = [];
$('.tooltipOpen').each(function(index){
tooltips.push($(this).data('idrow'))
})
console.log("tooltips length: " + tooltips.length)
if(tooltips.length==0){
// console.log("tooltip length is 0 so we're returning false")
return false
}
clientSubmit.OpenConversations = tooltips
clientSubmit.tablename = tablename
clientSubmit.CurrentData = $('body').data('conversations')
console.log(clientSubmit)
$.ajax({
type: 'POST',
url: '/conversations.php?loadNew=1',
data: clientSubmit,
timeout: 25000,
success: function(data){
console.log('success')
data=JSON.parse(data)
console.log(data)
$('body').data('conversations', data)
},
complete: function(status, jqXHR){
if(tooltips.length==0){
// console.log("tooltip length is 0 so we're returning false")
return false
}
else
{
doConversationsAjaxLongPoll(tablename);
}
}
});
updateConversations()
}
I don't doubt that there are faaaar better ways of doing this but I have worked around the problem by having a random number generated by the click function, stored in $('body').data('random') which is then passed to the poll function. When the poll function loops it checks if the random number it was passed matches the one in data-random and returns false if it doesn't.
Related
I am having a problem in my ajax or i don't know if it is a problem with ajax. I have an ajax code to get a value from label and concat it in my fresh data from database. Everytime i refresh the page, it outputs different. Sometimes it works fine, and sometimes it doesn't.
I am having my trouble in this part :
else {
value = value + "-"+init;
$('#checkID').text(value);
$("#checkID").css('visibility','visible');
}
sometimes it outputs 1-0 and sometimes the output became -0.
I am thinking of var value = $('#clinicID').html(); cannot concat with my -0 where the 1 of the output 1-0 is came from value variable
Here is my ajax full code :
function getcheckupID() {
var init = 0;
var value = $('#clinicID').html();
$.ajax ({
url: siteurl+"myclinic/getcheckID",
type: "GET",
dataType: "JSON",
success: function(data) {
if(data.length>0) {
$('#checkID').text(data[0]['check_up_id']);
$("#checkID").css('visibility','visible');
}
else {
value = value + "-"+init;
$('#checkID').text(value);
$("#checkID").css('visibility','visible');
}
}
})
}
my document ready code:
$(document).ready(function() {
get_clinicID();
show_patients();
checkupme();
});
where checkupme() function got a nested getcheckupID() runtime
I suggested another way to get data in #clinicID that you can use
When you refresh the page, insert your #clinicID like:
<span id="clinicID" data-value="1-0"><span> or whatever data you wanna input.
Then in getcheckupID function you'll call:
function getcheckupID() {
var init = 0;
var value = $('#clinicID').attr("data-value");
// Your code ajax
}
If it still have problem, please check your echo when page generated. Maybe there're not any value to print.
Hope this help.
I'm working on my first simple chat application and this issue has me stuck. I know what I'm trying to do, but I end up overthinking it.
Basically, I have this heroku server going:
http://tiy-fee-rest.herokuapp.com/collections/blabberTalk
Whenever someone sends a message, it is added to this array.
My Issue:
I have it on a set interval so that every 2 seconds, it runs the getNewestMessages function. When this setInterval is working and someone sends a message, it will keep appending the last message they sent every 2 seconds. If I disable the setInterval and simply call the getNewestMessages function myself in a separate browser tab, this doesn't seem to happen. I want to make it so that the most recently sent message isn't constantly re-appended to the DOM when the setInterval is active.
This is the function I'm using to check for recent messages. It's pretty bloated, sorry about that:
getNewestMessages: function() {
$.ajax({
url: http://tiy-fee-rest.herokuapp.com/collections/blabberTalk,
method: 'GET',
success: function (data) {
// Finds Id of most recent message displayed in the DOM
var recentId = $('.message').last().data('id');
var prevMostRecent = 0;
var newMostRecent = [];
jQuery.each(data, function(idx,el){
if (el._id === recentId) {
// if one of the messages on the server has an Id equal to
// one of the messages in the DOM, it saves its index in a var
prevMostRecent = idx;
}
});
jQuery.each(data, function(idx,el){
if (idx < prevMostRecent) {
// if there are messages on the server with a lower index than
// the most recent message in the DOM, it pushes them to a new
// array. Basically, creates a new array of any messages newer
// than the last one displayed in the DOM.
newMostRecent.push(el);
}
});
for (var i = 0; i < newMostRecent.length; i++) {
console.log(newMostRecent[i]);
if (newMostRecent[i]._id === $('.message').last().data('id')) {
// My attempt at trying to remove the last DOM message from
// the array of newer messages. My main issue was that this
// whole function would keep appending the most recent message
// over and over again.
var result = _.without(newMostRecent, newMostRecent[i]);
console.log('MESSAGE TO BE EXCLUDED: ', newMostRecent[i]);
// If the array of newer messages contained the most recent
// DOM message, it removes it and sends it to be appended.
page.appendNewestMessages(result);
}
}
// If the array of newer messages DOESN'T contain the most recent
// DOM message, it just sends the whole array normally.
page.appendNewestMessages(newMostRecent);
},
error: function (err) {
}
});
}
Here is the append function:
appendNewestMessages: function(messagesToAppend) {
console.log(messagesToAppend.reverse());
_.each(messagesToAppend.reverse(), function(el, idx, arr) {
var newMessage = {
content: el.content,
timestamp: el.timestamp,
author: el.author,
userIcon: el.userIcon
}
$.ajax({
url: page.url,
method: 'POST',
data: newMessage,
success: function (data) {
page.addOneMessageToDOM(data);
},
error: function (err) {
console.log("error ", err);
}
});
})
}
Can anyone help me understand how to get the most recent messages from a server and append them to the DOM without any repeats? This has been driving me nuts.
Thanks for any and all help.
So here is my problem (and I have tried several suggestions found here at stackOverflow):
Scenario:
I am using the Gitlab API and I want to list all the "issues" of the bug tracker present on the system for a given project.
Problem:
This is all fine and good, however there is a paging system to do this since the ajax requesst is limited to 100 entries per response.
So I have to do something like this:
$.ajax({
url: "https://mygitlabURL/api/v3/projects/97/issues",
type: "GET",
data: {
private_token: "mytoken",
per_page: 100,
page: 1
}
This will give me back 100 entries. What I need to do is add these entries to a list, and check: "was there fewer than 100 entries in the response?" if so I can stop doing requests.
I need however to get the complete list before I can move on with my code, so I tried using $.when() function and do my requests in a function of its own. In this function I have tried using:
closures like in this answer
recursion like suggested in another answer (don't have the link)
while loop since, oh well, why not
The problem with all the solutions is that, beeing asynchronous, I end up receiving a response and my $.when() function executes before I have any response from the server.
Is there a way to do this?
Here is the latest code (with recursion) I have tried:
$(function () {
$("button").on("click", function () {
$.when(func1(), func2()).then(finishedFunc);
});
});
var func1 = function (pageNr) {
pageNr = pageNr || 1;
megaList = [];
// Get server values
$.ajax({
url: "https://mygitlabURL/api/v3/projects/97/issues",
type: "GET",
data: {
private_token: "myToken",
per_page: 100,
page: pageNr
},
success: function (issuesList) {
console.log("Page number: " + pageNr);
megaList = [pageNr];
if (issuesList.length < 100) {
return megaList;
}
pageNr = pageNr +1 ;
var received = func1(pageNr);
megaList = $.merge(megaList, received);
return megaList;
}
});
}
var func2 = function () {
return 20;
}
var finishedFunc = function (resp1, resp2) {
console.log("Responses were resp1: " + resp1 + " and resp2: " + resp2);
}
And I always get something like:
"Responses were resp1: undefined and resp2: 20"
And I am expecting something like:
"Responses were resp1: [1, 2, 3, 4, 5, ..., 27] and resp2: 20"
As stated before, I can't find any solutions that resolve my problem here in the forums, but if I might have overlooked something, please point me in the right way.
While reading the documentation.I came across this.
Pagination
When listing resources you can pass the following parameters:
page (default: 1) - page number
per_page (default: 20, max: 100) - number of items to list per page
Link headers are send back with each response. These have rel prev/next/first/last and contain the relevant URL. Please use these instead of generating your own URLs.
It automatically says that the Response that came back will contain rel prev/next/first/last. So you can easily check that link headers contain next rel or not and If it contain then directly call that url for more issues and If not that means it does not contain more issues.
Once you start to think in async terms the solution become pretty simple:
var megaList = [];
function loadList(page) {
page = Math.max(1, page);
$.ajax({
url: "https://mygitlabURL/api/v3/projects/97/issues",
type: "GET",
data: {
private_token: "myToken",
per_page: 100,
page: page
},
success: function (issuesList) {
console.log("Page number: " + page);
megaList = megaList.concat(issuesList);
if (issuesList.length >= 100) loadList(page+1);
}
});
}
loadList();
I'm trying to load additional modals from the server after the initial fetch with Paginator.clientPager
This is my collection, pretty much copy pasted from the example code on github.
return new (Backbone.Paginator.clientPager.extend({
model: model,
paginator_core: {
type: 'GET',
dataType: 'json',
url: '/odata/LibraryFile'
},
paginator_ui: {
// the lowest page index your API allows to be accessed
firstPage: 1,
// which page should the paginator start from
// (also, the actual page the paginator is on)
currentPage: 1,
// how many items per page should be shown
perPage: 2,
// a default number of total pages to query in case the API or
// service you are using does not support providing the total
// number of pages for us.
// 10 as a default in case your service doesn't return the total
totalPages: 5
},
server_api: {
// number of items to return per request/page
'$skip': function () { return this.perPage * (this.currentPage - 1) },
'$top': function () { return this.perPage },
},
parse: function (response) {
console.log(response);
return response.value;
}
}))();
I'm calling the initial fetch like so
myCollection.fetch({
success: function(){
myCollection.pager();
},
silent:true
});
Then, after the user has browsed trough the local pages with the clientPager, he probably wants to load in more pages, without deleting the first pages.
I try to achieve this like this, but for some reason, after i call pager(); the 2 new records are removed.
myCollection.currentPage = 2;
myCollection.fetch({
success: function(){
console.log(myCollection.length) // 4 models, with correct data
myCollection.pager();
console.log(myCollection.length) // the 2 new records are removed
},
silent:true,
remove: false // don't remove old records
});
What am i doing wrong, how can i load it 2 more pages with the Paginator.clientPager ?
I don't want to use requestPager because then i can't do in memory pre-caching, at least, i think.
In my experience, this is caused by the pager() method of Backbone.Paginator.clientPager. You can take a look at the code here:
Backbone.Paginator.clientPager
Lines 292 through to 294 show that the Backbone.Paginator.clientPager.origModels is only assigned to the current models (the one whose length you correctly tested in your illustrations above) if it's undefined. The problem is that by the time the user probably wants to load more pages without deleting the first, the origModels property would already be set as a result of the initial fetch.
This means you'd have to explicitly make origModels undefined again before pager() would act as you want. Note what happens later on line 296 of the source code (models is assigned to a copy of origModels). That's why your two new records were removed. The following code should work as you intended:
myCollection.currentPage = 2;
myCollection.fetch({
success: function(){
delete myCollection.origModels; // to ensure that origModels is overridden in pager() call below
myCollection.pager();
},
silent:true,
remove: false // don't remove old records
});
I'm making a fairly complex HTML 5 + Javascript game. The client is going to have to download images and data at different points of the game depending on the area they are at. I'm having a huge problem resolving some issues with the Data Layer portion of the Javascript architecture.
The problems I need to solve with the Data Layer:
Data used in the application that becomes outdated needs to be automatically updated whenever calls are made to the server that retrieve fresh data.
Data retrieved from the server should be stored locally to reduce any overhead that would come from requesting the same data twice.
Any portion of the code that needs access to data should be able to retrieve it easily and in a uniform way regardless of whether the data is available locally already.
What I've tried to do to accomplish this is build a data layer that has two main components:
1. The portion of the layer that gives access to the data (through get* methods)
2. The portion of the layer that stores and synchronizes local data with data from the server.
The workflow is as follows:
When the game needs access to some data it calls get* method in the data layer for that data, passing a callback function.
bs.data.getInventory({ teamId: this.refTeam.PartyId, callback: this.inventories.initialize.bind(this.inventories) });
The get* method determines whether the data is already available locally. If so it either returns the data directly (if no callback was specified) or calls the callback function passing it the data.
If the data is not available, it stores the callback method locally (setupListener) and makes a call to the communication object passing the originally requested information along.
getInventory: function (obj) {
if ((obj.teamId && !this.teamInventory[obj.teamId]) || obj.refresh) {
this.setupListener(this.inventoryNotifier, obj);
bs.com.getInventory({ teamId: obj.teamId });
}
else if (typeof (obj.callback) === "function") {
if (obj.teamId) {
obj.callback(this.team[obj.teamId].InventoryList);
}
}
else {
if (obj.teamId) {
return this.team[obj.teamId].InventoryList;
}
}
}
The communication object then makes an ajax call to the server and waits for the data to return.
When the data is returned a call is made to the data layer again asking it to publish the retrieved data.
getInventory: function (obj) {
if (obj.teamId) {
this.doAjaxCall({ orig: obj, url: "/Item/GetTeamEquipment/" + obj.teamId, event: "inventoryRefreshed" });
}
},
doAjaxCall: function (obj) {
var that = this;
if (!this.inprocess[obj.url + obj.data]) {
this.inprocess[obj.url + obj.data] = true;
$.ajax({
type: obj.type || "GET",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: obj.data,
url: obj.url,
async: true,
success: function (data) {
try {
ig.fire(bs.com, obj.event, { data: data, orig: obj.orig });
}
catch (ex) {
// this enables ajaxComplete to fire
ig.log(ex.message + '\n' + ex.stack);
}
finally {
that.inprocess[obj.url + obj.data] = false;
}
},
error: function () { that.inprocess[obj.url + obj.data] = false; }
});
}
}
The data layer then stores all of the data in a local object and finally calls the original callback function, passing it the requested data.
publishInventory: function (data) {
if (!this.inventory) this.inventory = {};
for (var i = 0; i < data.data.length; i++) {
if (this.inventory[data.data[i].Id]) {
this.preservingUpdate(this.inventory[data.data[i].Id], data.data[i]);
}
else {
this.inventory[data.data[i].Id] = data.data[i];
}
}
// if we pulled this inventory for a team, update the team
// with the inventory
if (data.orig.teamId && this.team[data.orig.teamId]) {
this.teamInventory[data.orig.teamId] = true;
this.team[data.orig.teamId].InventoryList = [];
for (var i = 0; i < data.data.length; i++) {
this.team[data.orig.teamId].InventoryList.push(data.data[i]);
}
}
// set up the data we'll notify with
var notifyData = [];
for (var i = 0; i < data.data.length; i++) {
notifyData.push(this.inventory[data.data[i].Id]);
}
ig.fire(this.inventoryNotifier, "refresh", notifyData, null, true);
}
There are several problems with this that bother me constantly. I'll list them in order of most annoying :).
Anytime I have to add a call that goes through this process it takes too much time to do so. (at least an hour)
The amount of jumping and callback passing gets confusing and seems very prone to errors.
The hierarchical way in which I am storing the data is incredibly difficult to synchronize and manage. More on that next.
Regarding issue #3 above, if I have objects in the data layer that are being stored that have a structure that looks like this:
this.Account = {Battles[{ Teams: [{ TeamId: 392, Characters: [{}] }] }]}
this.Teams[392] = {Characters: [{}]}
Because I want to store Teams in a way where I can pass the TeamId to retrieve the data (e.g. return Teams[392];) but I also want to store the teams in relation to the Battles in which they exist (this.Account.Battles[0].Teams[0]); I have a nightmare of a time keeping each instance of the same team fresh and maintaining the same object identity (so I am not actually storing it twice and so that my data will automatically update wherever it is being used which is objective #1 of the data layer).
It just seems so messy and jumbled.
I really appreciate any help.
Thanks
You should consider using jquery's deferred objects.
Example:
var deferredObject = $.Deferred();
$.ajax({
...
success: function(data){
deferredObject.resolve(data);
}
});
return deferredObject;
Now with the deferredObject returned, you can attach callbacks to it like this:
var inventoryDfd = getInventory();
$.when(inventoryDfd).done(function(){
// code that needs data to continue
}
and you're probably less prone to errors. You can even nest deferred objects, or combine them so that a callback isn't called until multiple server calls are downloaded.
+1 for Backbone -- it does some great heavy lifting for you.
Also look at the Memoizer in Douglas Crockford's book Javascript the Good Parts. It's dense, but awesome. I hacked it up to make the memo data store optional, and added more things like the ability to set a value without having to query first -- e.g. to handle data freshness.