Dojo tree - delay until server returns data - javascript

I have a server function that generates JSON representing part of the file system.
The server function is called once the user has selected an item from a pull-down list.
So far so good.
My question is how do I display the tree ONLY when the JSON data has been returned from the server? Please make your answer as verbose and complete as possible as I'm not a javascript pro by any means!

var serverFunctionComplete = false;
var x = serverFunction();
while(!serverFunctionComplete) {
//just waiting
}
setTimeout(function() {
serverFunctionComplete = true;//if the server doesn't respond
}, 5000);
This should get you started.

You could make ajax request synchronous using the sync : true property in the xhr object. This stops other code from executing until the response is recieved from the server and the callback is done executing.
require(["dojo/request/xhr"], function(xhr){
xhr("example.json", {
handleAs: "json",
sync: true
}).then(function(data){
// Do something with the handled data
}, function(err){
// Handle the error condition
}, function(evt){
// Handle a progress event from the request if the
// browser supports XHR2
});
});
However, this is typically not the best practice as asynchronous loading as one of the great things about javascript and ajax. It would be reccomended to do display your tree in the callback function in the xhr so your script does not get hung up polling for a response.
require(["dojo/request/xhr"], function(xhr){
xhr("example.json", {
handleAs: "json"
}).then(function(data){
// Display your Tree!
}, function(err){
// Handle the error condition
});
});
For general asynchronous thread management, refer to Dojo's Deffered class.

Related

jQuery is not calling .done() when the ajax call is successful

We're building a node.js application using Express, but are separating our layers. So the browser is running pure jQuery and javascript, the web server and application server are Node.js and Express. We're using REST APIs between them all.
We're using jQuery 1.10.2
Since the application server cannot be open to the public, the browser must make API calls to the web server, which manages making the call to the application server and returning the results. Here is what that looks like...
// Proxy all other API calls to the backend server
var request = require('request');
app.all('/api/*', function (req, res) {
var targeturl = apihost+req.originalUrl;
console.log("Proxy: "+targeturl);
request({
url: targeturl,
method: req.method,
json: req.body
}, function (error, response, data) {
if(error) {
res.send(error);
res.end();
} else {
console.log("SUCCESS...");
console.log(data);
res.send(data);
res.end();
}
});
});
Our data is a two-level hierarchy of Flows and one or more child Milestones on each Flow. When someone clicks the Save link on our page to save a Flow and its Milestones, the Javascript pulls the Flow data from the page, then makes an AJAX call to save the Flow information first, then waits for the "done()" handler before pulling the Milestone information from the page and making a second AJAX call to save that data. Here is the saveFlow() function executed in the browser...
function saveFlow(organization_id, user_id, flow_id) {
var name = $('#Name').val();
var purpose = $('#Purpose').val();
console.log("Trying to save flow: " + flow_id);
if (name && name.length > 0) {
$.ajax({
url: "/api/flow",
type: 'POST',
datatype: 'json',
data: {
flow_id: flow_id,
name: name,
purpose: purpose,
organization_id: organization_id,
user_id: user_id,
timestamp: (new Date())
}
})
.fail(function (error) {
console.log("Could not save the flow: " + error.message);
})
.done(function (flow) {
console.log("Saving milestones for flow "+flow.id+" ["+flow_id+"]");
saveMilestones(flow.id);
});
} else {
console.log("Refusing to save a flow that has no name");
}
}
The proxy code shows me that this call succeeded...
SUCCESS...
{ success: true,
message: 'Flow Updated!',
id: '56de8e346d229b492a0954f9' }
But the proxy code also demonstrates that the next API call never takes place. Moreover, the log statement in the Done block is never executed, nor are any log statements placed inside the saveMilestones() function.
Clearly, the .done() handler is never being called in this case. I use the done() handler in other parts of my code and it works. I've compared the code but don't see any differences. The proxy code we're using is driving every other API call made from our pages successfully, so I don't think that it's failing to return the success status.
I've done a lot of searching on this site and tried every suggestion I could find - adding a explicit "json" datatype parameter, switching from ":success" to ".done()" - but nothing has worked yet. I even tried switching from done() to always() - a bad idea but I wanted to see if it would get called...and it wasn't called.
It's also worth noting that the .fail() handler is also not being executed.
Is it possible that the JSON block we're sending back to indicate success is somehow failing to make jQuery realize that we were successful? Or is it possible that the AJAX call is crashing when it tries to process the successful return? If so, how do I catch that to prove it and fix it?
Hope someone can help.

MockJax is not sending response to my AJAX request in JavaScript application

I am using a jQuery library called MockAjax which allows you to mock/test real AJAX calls.
In my application I can use my live app version of an AJAX request and MockAjax will intercept the AJAX request and respond with a Mock response!
I am also using another library called M<ockJson which is similar but instead allows you to generate a Mock JSON response.
Using both libraries together, my Application makes an AJAX request. MockAjax catches the AJAX request and then MockJson generates and returns a random JSON response.
In my past projects this has worked great with no issues until today...
Now that my application is working pretty good, I decided it;s time to restructure the JavaScript into a more structured version. (putting DOM events, tasks, etc into sections of code).
This is where my problem began....
In my new code,
my App makes an AJAX request.
MockAjax catches the AJAX request.
MockJson is called to get the JSON response
ERRORS this is where it all goes wrong...
At this last step, it should pass the JSON response back to the original AJAX calls Success function. It simply does not!
I get no errors or anything in the console. I set up a simple alert() in my AJAX calls success() function and it does not make it that far to even trigger the alert!
I am not sure if there is some sort of scope issue or what the problem could be. When my app was flat, all variables and functions in the glbal root level with no structure to the app at all...it all worked. As soon as I moved everything into Objects, etc....it all works except this 1 issue of not returning the MockAjax response back to the Real Ajax response!
I removed 95% of the app code and re-built it with just the very minimal to run this example problem. The demo of the problem is here... http://jsbin.com/vugeki/1/edit?js
App flow:
projectTaskModal.init(); is ran on page load
This fires off projectTaskModal.mockAjax.init(); which sets up the MockAjax and MockJson code
Then projectTaskModal.task.openTaskModal(projectTaskModal.cache.taskId); is ran which executes the AJAX request
AJAX POST Request is sent to /gettaskevents
MockAjax catches the request sent to /gettaskevents
MockAjax then calls MockJson to generate the JSON response. projectTaskModal.mockAjax.generateTaskEventsJson(); calls that function and I have it printing the JSON respionse into the console so I can see that it is generating it!
In my MockAjax code, var taskevents holds the JSON response and then set it to this... this.responseText = taskevents; ``this.responseTextI believe is what is supposed to be returned to the Applications originalAJAX` call. It seems that this is where the problem might be! It does not seem to be returning the response back to the original AJAX code that requested it in the first place!
Could this be some sort of scope issue?
var projectTaskModal = {
cache: {
taskId: 1,
projectId: '12345',
},
init: function() {
projectTaskModal.mockAjax.init();
//console.log(projectTaskModal.mockAjax.init.generateTaskEventsJson());
projectTaskModal.task.openTaskModal(projectTaskModal.cache.taskId);
},
task: {
openTaskModal: function(taskId) {
// Load Task Events/Comments Panel from AJAX Request
projectTaskModal.task.loadTaskEventsPanel(taskId);
},
/**
* Load Task Events/Comments from backend Database JSON
* #param {string} jsonServerEndpoint URL for AJAX to Request
* #return {string} Generated HTML of all Task Events built from JSON
*/
loadTaskEventsPanel: function(taskId) {
// Request Task Events and Comments using AJAX Request to Server
$.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
url: '/gettaskevents',
data: {
action: 'load-task-events',
task_id: projectTaskModal.cache.taskId
},
success: function(data) {
alert('TESTING AJAX SUCCESS CALLBACK WAS CALLED!');
console.log('function loadTaskEventsPanel(taskId) DATA: ');
console.log(data);
// Parse JSON data
var taskEventsJson = data;
var task_events = taskEventsJson.task_events;
// Loop over each Task Event record returned
$.each(task_events, function(i, event) {
console.log('commentID: ' + event.commentId);
console.log('create_at DateTime: ' + event.created_at);
});
}
});
},
},
mockAjax: {
init: function(){
// Adding the #EVENT_TYPE keyword for MockJSON Template Usage
$.mockJSON.data.EVENT_TYPE = [
'Comment Created',
'Task Status Changed',
'Task Completed'
];
// Mock AJAX response for AJAX request to /gettaskevents
$.mockjax({
url: '/gettaskevents',
contentType: 'text/json',
responseTime: 2900, // Simulate a network latency of 750ms
response: function(settings) {
console.log('mockJax POST to /gettaskevents :');
//console.log(settings);
//DEBUG('Get Task Events JSON', settings.data);
if(settings.data.value == 'err') {
alert('MockAjax Error');
this.status = 500;
this.responseText = 'Validation error!';
} else {
alert('MockAjax Success');
//var taskevents = generateTaskEventsJson();
var taskevents = projectTaskModal.mockAjax.generateTaskEventsJson();
this.responseText = taskevents;
console.log(taskevents);
}
}
});
},
// Generate Mock JSON Response to load fake Task Evewnt/Comments JSON for Mock AJAX request
//var generateTaskEventsJson = function () {
generateTaskEventsJson: function() {
var mockTaskJson = $.mockJSON.generateFromTemplate({
"task_events|10-14" : [{
"commentId|+1" : 100000000,
"projectId|+1" : 100000000,
"taskId|+1" : 100000000,
"userId|+1" : 100000000,
"created_at" : "#DATE_YYYY-#DATE_MM-#DATE_DD",
"event_type" : "#EVENT_TYPE",
"userName" : "#MALE_FIRST_NAME #LAST_NAME",
"description" : "#LOREM_IPSUM #LOREM_IPSUM"
}]
});
//DEBUG('Generate Mock Task Events JSON', mockTaskJson.task_events);
//console.log(mockTaskJson.task_events);
//return mockTaskJson.task_events;
return mockTaskJson;
}
},
};
$(function() {
projectTaskModal.init();
});
Your JSBin example shows that you are using a very old version of Mockjax (1.5.0-pre). The latest is 1.6.2, released quite recently (I'm the core maintainer now). Below is a link to an updated JSBin where everything appears to be working just fine. The old version of Mockjax that you were running was created before jQuery 2.0 existed, and thus does not support it (1.6.2 does).
http://jsbin.com/qucudeleve/1/
So... update your Mockjax version to use Bower, npm, or just Rawgit (https://rawgit.com/jakerella/jquery-mockjax/master/jquery.mockjax.js) from the primary account (mine) versus your own fork which is extremely out of date!
Good luck.

Is this a scope issue in JavaScript / JQuery? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is it posible to use ajax respone outside of it?
I've created the following JavaScript routine that goes to a WCF oData service and gets some data. In the success element I get the results into the results variable and alert them - I see that there are objects returned. When I run the second alert, outside of the ajax call and before returning the results, the results variable is "undefined".
Can anyone please tell me where I'm going wrong?
function retrieveJsonpODataSet(baseUrl, query)
{
var oDataUrl = baseUrl + "?$format=json&$callback=?";
var results;
$.ajax(
{
url: oDataUrl,
contentType: 'application/json; charset=utf-8',
type: 'GET',
dataType: 'jsonp',
async: false,
success:
function (data, textStatus, xhr)
{
results = data.d;
alert(results); // This shows the results
},
error:
function (xhr, textStatus, errorThrown)
{
alert("Query failed.\n\n" + oDataUrl + "\n\n" + errorThrown);
results = null;
}
});
alert(results); // This shows "undefined"
return results;
}
Please ignore the query parameter - I've not finished the routine yet.
EDIT
Initially I had no async:false in the ajax call. I've added that now but it doesn't fix the problem.
The ajax call is an asynchronous operation. It fires and your code does not stop at it. So results is returned which at that point is undefined. What you need to do is to pass callback to the function.
function retrieveJsonpODataSet(baseUrl, query, callback) {
/* some code */
$.ajax({
/* some settings */
success: function(res) {
/* some code */
callback(results);
}
});
}
Now you use it like this
retrieveJsonpODataSet(baseUrl, query, function(res) {
/* Hurray, I have result now in res variable! */
});
DO NOT USE async: false OPTION! It blocks ALL scripts untill the call finishes... and what if it does not finish at all?? You will be blocked forever.
EDIT
I've missed that the request is JSONP. In that case async: false won't even work (it does not work for cross-domain requests and JSONP). So you have to use callbacks anyway.
A fellow Richard!
This isn't a scope issue, but more of an execution one. Both the success and error options are event handlers, and run asynchronously (hence it being called AJAX). This essentially means that the alert(results) and return results can, and likely will, get executed before the success or error events are triggered.
Your ajax is async, so the alert executes before the ajax completes. You need to set the ajax call async property to false in order for script to halt the execution until ajax request is made & processed.
However, jQuery docs says:
async
Default: true
By default, all requests are sent asynchronously (i.e. this is set to true by default). If you need synchronous requests, set this option to false. Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation. Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active. As of jQuery 1.8, the use of async: false is deprecated.
AJAX request are sent, without the script waiting for a response, that's what Dave Newton means by A-synchronus, put the alert inside the success callback function, and you'll see what the actual response is.
alternatively, you can specify the async property, and set it to false, to force your script to wait for the response, before continuing.

JQuery Callback Never Called

I have the following JS/JQuery snippet:
function add_item() {
var item = $("input:text[name='new_item']").val();
$.post("{{ url_for('add_item') }}",
{'item' : item},
function(data) {
alert(':}');
});
}
It performs a simple AJAX request to a Flask webserver and displays an alert box on success (the data always returns a JSON snippet). The AJAX request adds a field to a SQLite database and returns. On my dev box, this request completes very quickly using asynchronous requests. However, on another server this request takes a few seconds (less than the default timeout, though) using asynchronous requests.
Problem: When the request takes a long time to complete using asynchronous requests, the callback is never called. When I change the AJAX settings to be synchronous, the callback is always called.
Thank!
I would try the $.ajax() function over the post one. Seems to have been more maintained - 1.6.2 also seems to have some issues, so try 1.6.1 if you need to: http://api.jquery.com/jQuery.ajax/
Use the error method to find out what error you're getting.
function add_item() {
var item = $("input:text[name='new_item']").val();
$.post("{{ url_for('add_item') }}",
{'item' : item},
function(data) {
alert(':}');
}).error(function(jqXHR, textStatus, errorThrown) { alert("error"); });
}

javascript: handling client side initiated objects that the server hasn't assigned an id to

I'm developing a javascript application and I'm trying to make server side syncing as automagic and unobtrusive as possible
The problem I'm facing is that when creating an object client side I can immediately send the create ajax request and while I'm waiting for it to return add the object to the ui where necessary
However since the object has no id until the server responds I can't perform any update or destroy actions on it until the server responds and updates its id
What is the best way of dealing with this problem?
Some code if it helps:
create_object = function() {
return {
save: function() {
if (this.isNew()) {
this.create();
} else {
this.update();
}
},
isNew: function() {
return (this.id === undefined || this.id === null);
},
update: function () {
$.ajax({
url: '/update_object/'+this.id+'.json',
type: 'post',
dataType: 'json',
data: this
});
},
create: function () {
var object = this;
$.ajax({
url: '/create_object',
type: 'post',
dataType: 'json',
data: this,
success: function(data) {
object.id = data.id;
}
});
},
destory: function () {
$.ajax({
url: '/destroy_object/'+this.id+'.json',
type: 'get',
dataType: 'json'
});
}
};
};
var object = create_object();
object.message = "Foo!";
object.save();
// this will create a new object until object.save has responded
object.message = "Bar!";
object.save();
// this wont work until object.save has responded
object.destroy();
This is not really a problem. AJAX is asynchronous by nature (Asynchronous Javascript and XML). You will have to wait until you get a response from the server to update your UI.
If you must update the UI immediately, you can create an element with a placeholder ID that you can then update with the actual ID once the server responds.
UPDATE
This is in response to your comment. Webapps are not purely client-side applications. They fit into the client-server paradigm. You can try using a synchronous post, but this has the side-effect of tying up the browser until the server responds (this is due to the fact that Javascript is single-threaded).
That being said, the response from the server should be pretty quick unless your network is slow or if whatever post your sending to the server results in some computationally-intensive operation.
Going by the placeholder route will (as you have realized) result in all sorts of new problems that you have to deal with. You have to ensure that they are unique. Now what if they perform some operation on the client-side element before the server responds with an id? The server won't know what the placeholder id means. There are ways around this (queuing requests is one way), but it will make your code a whole lot more complicated. I don't think the tradeoff is worth it just to make the UI update faster.
An option is to provide some visual feedback to the user that the app is "working" (i.e., while you are waiting for a response from the server). A spinner, for example.
As far as stacking the AJAX requests, that is one approach that you can take using the jQuery message-queuing plugin.

Categories

Resources