Kendo scheduler: Ajax call while dragging an event - ajax

I was wondering if anyone has a working example of how to make a async ajax call while dragging an scheduler event. I have a horizontal grouping on different locations (resources in the scheduler) and want to update disabled timeslots on every location while using drag and drop. I have tried a few approaches with moveStart, but none of which made any progress.
Here is what I'm trying. The sync function is calling my API controller and should update the scheduler with the new disabled timeslots (slottemplate), but it never gets to the '$ajax'-part of the function (it's like it gets blocked).
moveStart: function (e) {
console.log(e.event);
selectedActivityId = e.event.activityId;
syncDisabledTimes(e.event.start);
syncDisabledTimes:
var asyncAjaxCalls = [];
for (var i = 0; i < selectedLocations.length; i++) {
asyncAjaxCalls.push(getAsyncCall(selectedLocations[i].locationId, e.event.start));
}
ajaxIsRdy = false;
$.when.apply($, asyncAjaxCalls).done(function () {
ajaxIsRdy = true;
var scheduler = data();
scheduler.view(scheduler.view().name);
});
getAsyncCall:
var asyncCall = $.ajax({
type: "GET",
url: "/api/v1/DisabledTimeTableUnits/",
data: {
'mode': DisabledTimeTableUnitsMode.OnlyDisabledTimes,
'filterOnOutsideTimeTableSet': true,
'filterOnSeasonLimits': true,
'filterOnOpeningHours': true,
'filterOnBookinglock': true,
'filterOnActivity': selectedActivityId === null ? false : true,
'utcTime': true,
'start': moment(_date).startOf('week').isoWeekday(1).toISOString(),
'end': moment(_date).endOf('week').isoWeekday(7).toISOString(),
'locationId': locationId,
'activityId': selectedActivityId
}
}).done(function (disabledTimeUnits) {
for (var i = 0; i < selectedLocations.length; i++) {
if (selectedLocations[i].locationId === locationId) {
selectedLocations[i].disabledTimeSlots = disabledTimeUnits;
}
}
});
return asyncCall;

MoveStart will fire once as you start dragging. Move will continuously fire as you drag the event around.
However, I would recommend that you perform your server side validation upon the MoveEnd event.

Related

Execute validation with button

I'm looking to execute the Handsontable validation on the click of a button instead of on cell change. Something like this: validateCells() (return bool isValid). This function doesn't seem to be working for me.
var
data = [],
container = document.getElementById('hot-Handsontable'),
save = document.getElementById('save'),
hidden = document.getElementById('hot-Handsontable-value'),
hot,
hotIsValid = true,
emailValidator;
emptyValidator = function(value, callback) {
callback(false);
};
hot = Handsontable(container, {
data: data,
minRows: 1,
minCols: 21,
maxCols: 21,
minSpareRows: 1,
stretchH: 'all',
colHeaders: ['Test'],
columns: [{data:'Test',allowInvalid:true, validator: emptyValidator}]
});
// exclude empty rows from validation
$('.title-block .united-alert a[href^=#Handsontable]').click(function() {
var href = $(this).attr('href');
var row = href.getIdIndex();
var prop = /([^__]*)$/.exec(href)[0];
hot.selectCellByProp(parseInt(row), prop);
return false;
});
// Save event
Handsontable.Dom.addEvent(save, 'click', function(e) {
var test = hot.validateCells(); // test is undefined
if (hotIsValid === true) {
hidden.value = JSON.stringify(hot.getData());
} else {
e.preventDefault();
}
});
What you should be doing, instead of var test = hot.validateCells() is the following:
// Save event
Handsontable.Dom.addEvent(save, 'click', function(e) {
hot.validateCells(function(hotIsValid) {
if (hotIsValid === true) {
hidden.value = JSON.stringify(hot.getData());
} else {
e.preventDefault();
}
})
});
Notice that validateCells takes a callback and returns undefined. This is why you see test as undefined. One other thing to note is that the callback is executed for every cell in your table so be careful with it.

Updating a computed observable only sometimes

I have a computed observable that makes AJAX calls based on other data (in a computed observable). The resulting data is used to populate part of the UI. Sometimes that part of the UI is hidden and I'd like to avoid the AJAX calls when it's hidden. Right now I have the following, but it updates whenever isVisible becomes true:
this.loadData = ko.computed(function() {
if (this.isVisible()) {
this.isProcessing(true);
var self = this;
$.when.apply($, ko.utils.arrayMap(this.parent.data.filteredSelectedDatasetLinks(), function(datasetLink) {
return $.ajax({
url: datasetLink.getDownloadUrl('.json'),
success: function(data) {
//... do stuff with the data
}
});
}))
.done(function() {
self.isProcessing(false);
});
}
}, this);
So obviously I need to split this up somehow, but I haven't figured out how to do it. To reiterate, when isVisible is false, no updates should happen. When isVisible is true, updates happen whenever filteredSelectedDatasetLinks changes. When isVisible becomes true, updates happen if filteredSelectedDatasetLinks changed while it was false.
Presumably you want to call your ajax when the filteredSelectedDatasetLinks is changed (and only if visible?). I think the best way to do this is to make that dependency explicit using the subscribe function... (I have simplified slightly and fixed issue with your final 'this')
this.filteredSelectedDatasetLinks.subscribe(function() {
if (this.isVisible()) {
this.isProcessing(true);
var self = this;
$.when.apply($, ko.utils.arrayMap(this.filteredSelectedDatasetLinks(), function(datasetLink) {
return $.ajax({
url: datasetLink.getDownloadUrl('.json'),
success: function(data) {
//... do stuff with the data
}
});
}))
.done(function() {
self.isProcessing(false);
});
}
}, this);
The issue with your original attempt is that ko.computed runs the function once and automatically works out which observables it needs to subcribe to. In your case this included the isVisible observable (which is not what you wanted). But making it explicit with the subscribe call directly you no longer have to worry about isVisible firing the callback.
Here is what I ended up using based on RP Niemeyer's comments.
this.trackData = ko.computed(function() {
this.parent.data.filteredSelectedDatasetLinks(); // for notification
this.isDataDirty(true);
}, this);
this.loadData = ko.computed(function() {
if (this.isVisible() && this.isDataDirty()) {
this.isDataDirty(false);
this.isProcessing(true);
var self = this;
$.when.apply($, ko.utils.arrayMap(this.parent.data.filteredSelectedDatasetLinks.peek(), function(datasetLink) {
return $.ajax({
url: datasetLink.getDownloadUrl('.json'),
success: function(data) {
//... do stuff with the data
}
});
}))
.done(function() {
self.isProcessing(false);
});
}
}, this);

Conditionally pause Javascript to wait for ajax

The variable ajaxdata is modified within the success function, if that hasn't been done yet, I would like to wait 2 seconds, then continue without it.
The use case is for a jqueryui autocomplete field. The autocomplete source is an ajax request, but if the user types quickly, and exits the field before the list loads, the field remains unset. Using the 'change' event on the autocomplete I check if the user entered a valid option without selecting it, but this doesn't work if the source hasn't loaded when the change event fires. So I would like to put a delay in the change function which waits, if the source (stored in the variable 'ajaxdata') is empty.
code:
input.autocomplete({
source: function (request, response){
$.ajax(
{
type: "GET",
url: "/some/url",
dataType: "json",
success: function(data){
response($.map(data,function(item){
return{
label: item.label,
value: item.value
}
}));
ajaxdata = data;
}
}
);
// ajaxopts = ajaxsource(request,response,ajaxurl,xtraqry)
},
change: function(event, ui) {
if (!ui.item) {
// user didn't select an option, but what they typed may still match
var enteredString = $(this).val();
var stringMatch = false;
if (ajaxdata.length==0){
/// THIS IS WHERE I NEED A 2 SECOND DELAY
}
var opts = ajaxdata;
for (var i=0; i < opts.length; i++){
if(opts[i].label.toLowerCase() == enteredString.toLowerCase()){
$(this).val(opts[i].label);// corrects any incorrect case
stringMatch = true;
break;
}
}
}
},
});
Edit:
To be more specific about the problem: This delay needs to be conditional. Meaning that if the data is already loaded (either because it came from a static source, or from an earlier ajax call) I do not want to have a delay.
If I'm understanding you properly, I think you just want to check and see if ajaxdata has been populated; but if it hasn't, only wait two more seconds and then just proceed without it.
Try this:
change: function(event, ui) {
if (!ui.item) {
// user didn't select an option, but what they typed may still match
if (ajaxdata.length==0){
/// THIS IS WHERE I NEED A 2 SECOND DELAY
//pass in 'this' so that you can use it
setTimeout(function() {correctCase(this);}, 2000);
}
}
}
. . . . .
function correctCase(inThis){
//I'm not sure what this variable does. do you really need it???
var stringMatch = false;
var enteredString = $(inThis).val();
//you still want to be sure that ajaxdata is not empty here
if (ajaxdata.length==0){
var opts = ajaxdata;
for (var i=0; i < opts.length; i++){
if(opts[i].label.toLowerCase() == enteredString.toLowerCase()){
$(inThis).val(opts[i].label); // corrects any incorrect case
stringMatch = true; //this variable doesn't seem to do anything after this???
break;
}
}
}
}
I'm not really sure what it is you're trying to do, but I'm pretty sure something like this would be a better way of doing it :
input.autocomplete({
source: function(request, response) {
return $.ajax({
type: "GET",
url: "/some/url",
dataType: "json"
});
},
change: function(event, ui) {
if (!ui.item) {
// user didn't select an option, but what they typed may still match
var enteredString = this.value;
var stringMatch = false;
//make sure ajax is complete
this.source().done(function(data) {
var opts = $.map(data, function(item) {
return {
label: item.label,
value: item.value
}
});
for (var i = 0; i < opts.length; i++) {
if (opts[i].label.toLowerCase() == enteredString.toLowerCase()) {
$(this).val(opts[i].label); // corrects any incorrect case
stringMatch = true;
}
}
});
}
}
});​
By default, JavaScript is asynchronous whenever it encounters an async function, it queued that function for later.
But if you want a pause js(ajax call or anything) for you can do it use promises
Case 1: output hello(will not wait for setTimeout)
https://jsfiddle.net/shashankgpt270/h0vr53qy/
//async
function myFunction() {
let result1='hello'
//promise =new Promise((resolve,reject)=>{
setTimeout(function(){
resolve("done");
result1="done1";
}, 3000);
//});
//result = await promise
alert(result1);
}
myFunction();
case 2: output done1(will wait for setTimeout)
https://jsfiddle.net/shashankgpt270/1o79fudt/
async function myFunction() {
let result1='hello'
promise =new Promise((resolve,reject)=>{
setTimeout(function(){
resolve("done");
result1="done1";
}, 3000);
});
result = await promise
alert(result1);
}
myFunction();

AJAX Thread management in jQuery?

forgive me if this has been covered, but I promise to have searched beforehand.
I got a chain of AJAX get-requests updating columns every 30 seconds.
On the basis of these updates, a button to load new content is displayed,
again loading new content and resetting the related session variable.
Of course, collisions occur.
ATM, I block each column individually through variables which are freed up again after AJAX success, while setInterval'ing if a variable is blocked.
Now, is there any elegant way to do this? I've fiddled with deferred objects and .ajaxStop but to no avail. I read somewhere, that jQuery 1.5 / $.done() etc. should introduce some kind of thread management for Ajax calls? How can I leverage this?
//timed update
function refresh() {
if (BLOCKED != 1) {
BLOCKED = 1;
$.ajax({
url: 'update',
success: function(data) {
BLOCKED = 0;
}
});
}
//click function
function load() {
if (BLOCKED != 1) {
BLOCKED = 1;
$.ajax({
url: '/load'
});
} else setTimeout("load()", 500);
}
I'd suggest this that gets rid of the timer polling. This sets a flag when a load is pending and when the update finishes, it checks to see if the next load is pending rather than polling for that:
// global state
var inFlight = false;
var pendingLoad = false;
function checkPendingLoad() {
if (pendingLoad) {
pendingLoad = false;
load();
}
}
function refresh() {
if (!inFlight) {
inflight = true;
$.ajax({
url: 'update',
complete: function(data) {
inFlight = false;
checkPendingLoad();
}
});
}
}
//click function
function load() {
if (!inFlight) {
inFlight = true;
$.ajax({
url: '/load',
complete: function(data) {
inFlight = false;
checkPendingLoad();
}
});
} else {
pendingLoad = true;
}
}

backbone: issue an ajax call before resetting a collection

Right now I have a collection that fetches value, and after that every view attached to the reset event get rendered again
the problem is that I also have to issue another query to fetch the total number of records retrieved, and only after that ajax call is completed the reset event should be triggered
is more clear with a bit of code:
fetch: function() {
options = { data: this.getParams() };
this.fetch_total();
return Backbone.Collection.prototype.fetch.call(this, options);
},
fetch_total: function() {
var that = this;
var options = {
url: this.url + '/count',
data: this.getParams(),
contentType: 'application/json',
success: function(resp, status, xhr) {
that.total = parseInt(resp);
return true;
}
};
return $.ajax(options);
}
as you can see, I have to issue a get to localhost/myentity/count to get the count of entities...
The thing is I need the collection.total varaible to be updated before refreshing the views, that means I need both request, the GET to localhost/myentity and to localhost/myentity/count, to be completed before refreshing all the views...
any idea how can I achieve it???
If your $ of choice is jQuery>1.5, you could take advantage of the deferred object to manually trigger a reset event when both calls have completed. Similar to your answer, but a bit more readable and without chaining the calls:
fetch: function() {
options = {silent: true, data: this.getParams()};
var _this = this;
var dfd_total = this.fetch_total();
var dfd_fetch = Backbone.Collection.prototype.fetch.call(this, options);
return $.when(dfd_total, dfd_fetch).then(function() {
_this.trigger('reset', _this);
})
},
fetch_total: function() {
// what you have in your question
}
And a Fiddle simulating these calls http://jsfiddle.net/rkzLn/
Of course, returning the results and the total in one fetch may be more efficient, but I guess that's not an option.
I think #nikoshr's answer is a good one so that you don't have to modify your API. If you think that you want to lessen your calls to the server, then consider returning an object from that endpoint that has paging information.
{
count: 1243,
page: 3,
per_page: 10,
results: [
...
]
}
and then overriding the collection's parse functionality
parse: function(res) {
this.count = res.count;
this.page = res.page;
this.per_page = res.per_page;
// return the collection
return res.results;
}
RESOURCES
http://backbonejs.org/#Collection-parse
I think I found a way to do it. What I did was to silently fire the fetch call, without triggering the 'reset' event
There, from the callback, I issue the fetch of the total (GET to localhost/myentity/count)
and from the total callback, I finally trigge the reset event
in code is something like this:
fetch: function() {
var that = this;
options = {
// will manually trigger reset event after fetching the total
silent: true,
data: this.getParams(),
success: function(collection, resp) {
that.fetch_total();
}
};
return Backbone.Collection.prototype.fetch.call(this, options);
},
fetch_total: function() {
var that = this;
var options = {
url: this.url + '/count',
data: this.getParams(),
contentType: 'application/json',
success: function(resp, status, xhr) {
that.total = parseInt(resp);
// manually trigger reset after fetching total
that.trigger('reset', that);
return true;
}
};
return $.ajax(options);
}
This is my first attempt, I wonder if there's an easier way

Resources