jquery deferred with async calls - ajax

Please find the below code snippet:
HTML:
<div>
<span> First Name : <input type="text" id="firstName" name="First Name"/></span>
</div>
<br/>
<div>
<span>Student Id: <input type="text" id="studentId" name="studentId"/></span>
<span>Teacher Id: <input type="text" id="teacherId" name="teacherId"/></span>
</div>
<br/>
<div>
<span>Student Name : <input type="text" id="stdLastName" name="stdLastName"/></span>
<span>Student Age :<input type="text" id="stdAge" name="stdAge"/></span>
</div>
<br/>
<div>
<span>Teacher Name : <input type="text" id="tchrLastName" name="tchrLastName"/></span>
<span>Teacher Age : <input type="text" id="tchrAge" name="tchrAge"/></span>
</div>
<br/>
<input type="button" value="Submit" id="submit"/>
Javascript:
$('#firstName').focus();
var d1= new $.Deferred();
$('#firstName').blur(populatePage());
//called on blur of first name
function populatePage() {
$.when(populateStdDetails(),populateTchrDetails()).done(function(resp1, resp2){
$('#stdLastName').val(resp1[0].stdName);
$('#stdAge').val(resp1[0].age);
$('#tchrLastName').val(resp2[0].stdName);
$('#tchrAge').val(resp2[0].age);
console.log('All details populated....');
d1.resolve();
});
return d1;
}
//first ajax call
function populateStdDetails() {
if($('#firstName').val() != '' && $('#studentId').val() !='') {
return $.ajax({
url : '/someURL?studentId='+studentId+'&firstName='+firstName,
type :'GET',
contentType:'json'
});
}
}
//second ajax call
function populateTchrDetails() {
if($('#firstName').val() != '' && $('#teacherId').val() !='') {
return $.ajax({
url : '/someURL?teacherId='+teacherId+'&firstName='+firstName,
type :'GET',
contentType:'json'
});
}
}
$('#submit').click(function(e){
//wait for the ajax calls to be completed
$.when(populatePage()).done(function(e){
console.log('All done !!!!');
//Move to next page;
});
});
The First Name text field has an onblur event attached which works fine in usual scenario but when the focus is on "First Name" and "Submit" is clicked, the submit function is called instead of waiting for the onblur event to be completed.

You have placed deferred.resolve in wrong places in your timeout functions. Do it like this way:
function doSomething(deffered) {
$('#log').append('doSomething');
deferred.resolve();
return deferred;
};
function ajaxRequests1(deferred) {
setTimeout(function(){
$('#log').append('......ajaxRequests1');
deferred.resolve();
}, 1000);
return deferred;
};
function ajaxRequests2(deferred) {
setTimeout(function(){
$('#log').append('.....ajaxRequests2');
deferred.resolve();
}, 5000);
return deferred;
};
var func1 = function () {
var promise = new $.Deferred();
ajaxRequests1(promise);
return promise;
}
var func2 = function () {
var promise = new $.Deferred();
ajaxRequests2(promise);
return promise;
}
var stepFinal = function() {
var promise = new $.Deferred();
doSomething(promise);
return promise;
}
$.when(func1().promise(), func2().promise())
.done(function () {
stepFinal().done();
});

OK, if you want populatePage() to be called when focus leaves #firstname, and if the user also clicked on the submit button and you want the submit operation to wait for that blur action to finish, you can do this:
$('#firstName').blur(function(e) {
// call populatePage and set the resulting promise as a data item so
// the submit handler can get access to it
var self = $(this);
var p = populatePage();
self.data("blurPromise", p);
// when this promise is done, clear the blurPromise
p.always(function() {
self.removeData("blurPromise");
});
});
//called on blur of first name
function populatePage() {
return $.when(populateStdDetails(),populateTchrDetails()).done(function(resp1, resp2){
$('#stdLastName').val(resp1[0].stdName);
$('#stdAge').val(resp1[0].age);
$('#tchrLastName').val(resp2[0].stdName);
$('#tchrAge').val(resp2[0].age);
console.log('All details populated....');
});
}
//first ajax call
function populateStdDetails() {
if($('#firstName').val() != '' && $('#studentId').val() !='') {
return $.ajax({
url : '/someURL?studentId='+studentId+'&firstName='+firstName,
type :'GET',
contentType:'json'
});
} else {
// just return already resolved promise
return $.when();
}
}
//second ajax call
function populateTchrDetails() {
if($('#firstName').val() != '' && $('#teacherId').val() !='') {
return $.ajax({
url : '/someURL?teacherId='+teacherId+'&firstName='+firstName,
type :'GET',
contentType:'json'
});
} else {
return $.when();
}
}
$('#submit').click(function(e){
// get blur promise or dummy resolved promise
var p = $("#firstName").data("blurPromise") || $.when();
p.then(function() {
// do your submit logic here
// The onBlur handler is done now
});
});
Things I've updated in your promise handling code:
Use the $.ajax() promises directly without wrapping them in yet another promise.
Use the $.when() promises directly without wrapping them in yet another promise.
When using an if statement to decide whether or not to execute an asynchronous operation, it is usually best to also return a promise in the else arm so your function consistently always returns a promise. If there's nothing else to do in the else clause, then a shortcut for returning an already resolved promise in jQuery is to just return $.when();.
Be warned that .done() is jQuery-specific and not standard promise behavior. If you're already on jQuery 3.x or higher, then you should probably switch to .then() and then your promises will behave like the promise standard.

Related

My textarea input is not be empty after send message using Vue

I am using laravel and Vue. I have created a messenger application. Everything is done. But I am facing a problem. That is, after press enter button, message goes to the desired person. But the message still be available in the input filed until I refresh the page.
Here is my html code.
<input type="hidden" v-model="conID">
<textarea class="col-md-12 form-control" v-model="msgFrom" #keydown="inputHandler"
style="margin-top: 15px;border: none; resize: none;"></textarea>
Here is my Vue code.
inputHandler(e){
if(e.keyCode === 13 && !e.shiftkey){
e.preventDefault();
this.sendMsg();
}
},
sendMsg()
{
if(this.msgFrom){
axios.post('http://localhost:8000/sendMessage',{
conID:this.conID,
msg:this.msgFrom
})
.then(function(response){
console.log(response.data);
if(response.status===200){
app.singleMsgs = response.data;
}
})
.catch(function (error){
console.log(error);
});
}
Can anyone please help me. Just how I can make textarea empty after sending a message.
Thank's in advance.
should be as easy as clearing it once the message is sent
clear the form with: this.msgFrom = '', because you're doing it within a promise function (and without an arrow function), you need to store the this scope and pass it; usually done using var self = this
example:
inputHandler(e) {
if (e.keyCode === 13 && !e.shiftkey) {
e.preventDefault();
this.sendMsg();
}
},
sendMsg() {
var self = this; // <--- store scope 'this'
if (this.msgFrom) {
axios.post('http://localhost:8000/sendMessage', {
conID: this.conID,
msg: this.msgFrom
})
.then(function(response) {
console.log(response.data);
if (response.status === 200) {
app.singleMsgs = response.data;
self.msgFrom = ''; // <--- and then clear form
}
})
.catch(function(error) {
console.log(error);
});
}
}

knockout computed not updating while change in view model being made from ajax

How much I know about knockout Js is that a computed gets updated anyhow depending on the viewmodel, but in my case its not happening. So basically I have a radio button which turns off and on and changes the date in the database, and the ajax calls returns and pushes the new date in the viewmodel so that the data changes.
So thats the summary. But the thing I want is that while the radio button is being updated I want a part of the html to change to active or disabled based on the radio button.
Firstly here is the HTML code.
<div class="col-sm-4">
<p>
<span data-bind="text : $data.basketStatusValue"></span>
</p>
</div>
<div class="col-sm-4">
<div class="on_off">
<input type="checkbox" data-bind="bootstrapSwitchOn: {
tocall: $root.changeActiveBasketStatus
}" />
</div>
</div>
Here is the JS code.
function MoneyInvestedViewModel(root /* root not needed */, money) {
var self = this;
self.ID = money.ID;
self.ORIG_ID = money.ORIG_ID;
self.Available = money.Available;
self.basketStatusValue = ko.computed (function () {
if (self.Available == '9999-12-01') {
return "Active";
} else {
return "Disabled";
}
});
};
And next is the code which is updating the view model moneyInvested . So the checkbox can show on or off.
self.changeActiveBasketStatus = function (bindingContext) {
console.log(bindingContext);
var Id = bindingContext.$data.ORIG_ID;
var Available = bindingContext.$data.Available;
if (Available == '9999-12-01') {
$.ajax({
type: 'POST',
url: BASEURL + 'index.php/moneyexchange/changeBasketStatus/' + auth + "/" + Id + "/" + 1,
contentType: 'application/json; charset=utf-8'
})
.done(function (newAvailableDate) {
bindingContext.$data.Available = newAvailableDate;
})
.fail(function (jqXHR, textStatus, errorThrown) {
self.errorMessage(errorThrown);
})
.always(function (data){
});
} else {
$.ajax({
type: 'POST',
url: BASEURL + 'index.php/moneyexchange/changeBasketStatus/' + auth + "/" + Id + "/" + 0,
contentType: 'application/json; charset=utf-8'
})
.done(function (newAvailableDate) {
bindingContext.$data.Available = newAvailableDate;
})
.fail(function (jqXHR, textStatus, errorThrown) {
self.errorMessage(errorThrown);
})
.always(function (data) {
});
}
};
So basically the PROBLEM is that when all this update is done, the computed self.basketStatusValue does not get updated. So when I click the checkbox on, it doesnt show Active, or off for disabled, the checkbox is working perfectly, only the html $data.basketStatusValue is not updating through the computed function.
Just incase if necessary here is the code for the checkbox.
(function ($) {
ko.bindingHandlers.bootstrapSwitchOn = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var options = ko.utils.unwrapObservable(valueAccessor());
var tocall = ko.utils.unwrapObservable(options.tocall);
$elem = $(element);
$(element).bootstrapSwitch();
$(element).bootstrapSwitch('setState', bindingContext.$data.Available === '9999-12-01' ? true : false); // Set intial state
$elem.on('switch-change', function (e, data) {
tocall(bindingContext);
// valueAccessor()(data.value);
});
},
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
}
};
})(jQuery);
To summarize, all I want to do is have the $data.basketStatusValue have "active" or "disabled" when the checkbox is on or off.
A dirty trick you can use to pull this off is empty the whole observable and push it with the new data. But honestly its not the right way to use it. I am assuming right now thats its a array, but you can remove observables too. Just put the observable name instead of YourArray().
self.refresh = function(){
var data = YourArray().slice(0);
YourArray.removeAll();
self.YourArray(data);
};
And place this function right after the done function
.done(function(newAvailableDate) {
bindingContext.$data.Available = newAvailableDate;
// here self.refresh();
})
Your binding handler is wrong, let's start with that.
It should:
Set up Bootstrap switch on the element in init()
React to change in update()
Bind to an observabe (don't use a callback function). In our case the observable should contain the checkbox state, i.e. true or false.
Properly dispose of the Bootstrap widget when the time comes.
So, this would work better:
(function ($) {
ko.bindingHandlers.bootstrapSwitch = {
init: function (element, valueAccessor) {
var options = valueAccessor();
// set up bootstrap in init()
$(element).bootstrapSwitch().on('switch-change', function (e, data) {
options.value(data.value);
});
// see http://knockoutjs.com/documentation/custom-bindings-disposal.html
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).bootstrapSwitch("destroy");
});
},
update: function(element, valueAccessor) {
var options = valueAccessor();
// react to change in update()
$(element).bootstrapSwitch('setState', options.value());
}
};
})(jQuery);
Next we need to set up the viewmodel accordingly.
Available needs to be observable. View changes depend on it.
we need an observable that returns true or false, depending on Available
we need an observable that returns "Active" or "Disabled", depending on that
we need a function that updates the server on change (through a subscription)
like this:
function MoneyInvestedViewModel(money) {
var self = this;
self.ID = money.ID;
self.ORIG_ID = money.ORIG_ID;
self.Available = ko.observable(money.Available);
self.errorMessage = ko.observable();
self.BasketStatus = ko.computed(function () {
return self.Available() == '9999-12-01';
});
self.BasketStatusText = ko.computed(function () {
return self.basketStatus() ? "Active" : "Disabled";
});
// BEWARE: this is not actually correct (cicular dependency)
self.BasketStatus.subscribe(function () {
$.ajax({
type: 'POST',
url: BASEURL + 'index.php/moneyexchange/changeBasketStatus/' + auth + "/" + self.ID + "/" + 1,
contentType: 'application/json; charset=utf-8'
})
.done(function (newAvailableDate) {
self.Available(newAvailableDate);
})
.fail(function (jqXHR, textStatus, errorThrown) {
self.errorMessage(errorThrown);
});
};
}
Note: Subscribe to the correct observable to update the server with the proper value. It was not clear from your question what value the server update depends on.
And now it's straightforward to bind a view to that:
<div class="col-sm-4">
<p><span data-bind="text: BasketStatusText"></span></p>
</div>
<div class="col-sm-4">
<div class="on_off">
<input type="checkbox" data-bind="bootstrapSwitch: {
value: BasketStatus
}" />
</div>
</div>
if(self.Available == '9999-12-01'){ return "Active"; }else{ return "Disabled";}
The trouble with this line is that it does not look up the value of any observable or computed, and thus it does not cause the computed to ever be updated.
You need to make self.Available an observable and then do self.Available()
This is how computeds work, they are recomputed when any of their observable/computed dependencies change. Knockout will not see a simple property update like you are doing.

knockoutjs data bind hidden field value

I'm having a hidden field in a knockout template that its value gets updated with jquery. The problem is when trying to pass this value to the server with ajax, I get null value in the controller. But the html source code shows that the value of the hidden field is updated. If I replaced the hidden field with a textbox, it would work fine only when I enter text manually.
jQuery
function getFileDetail(fileID, fileName) {
$('#hdnFileName' + fileID).val(fileName);
$('#lblFileName' + fileID).text(fileName);
}
Here is the html knockout template:
<script type="text/html" id="fileTemplate">
<div data-role="fieldcontain">
<label data-bind="text: 'File Upload ' + ID, attr: { id: 'lblFileName' + ID }"></label><input type="button" value="Remove" data-bind="click: removeFile" />
</div>
<input type="hidden" name="hdnFileName" data-bind="attr: { id: 'hdnFileName' + ID, value: fileName }" />
</script>
ViewModel
function FileViewModel() {
var self = this;
self.ID = ko.observable();
self.fileName = ko.observable();
self.removeFile = function (file) { };
self.Files = ko.observableArray([{ ID: 1, fileName: "", removeFile: function (file) { self.Files.remove(file); }}]);
self.addNewFile = function () {
var newFile = new FileViewModel();
newFile.ID = self.Files().length + 1;
newFile.fileName = "";
newFile.removeFile = function (file) { self.Files.remove(file); };
self.Files.push(newFile);
//$("input[name='hdnFileName'").trigger("change");
}
}
function ViewModel() {
var self = this;
self.fileViewModel = new FileViewModel();
self.submitForm = function () {
$.ajax({
type: "POST",
url: "<%= Url.Action("MeetingPresenter")%>",
data: "{Files:" + ko.utils.stringifyJson(self.fileViewModel.Files) + "}",
contentType: "application/json",
success: function (data) {},
});
};
}
Your model property ID is an observable, so you need to 'unwrap' to get the value from it when you are concatenating, like this:
<input type="hidden" name="hdnFileName" data-bind="attr: { id: 'hdnFileName' + ID(), value: fileName }" />
and this:
<label data-bind="text: 'File Upload ' + ID(), attr: { id: 'lblFileName' + ID() }"></label>
If you are using knockout.js you don't neede to modify the DOM, you can just update the ViewModel and the DOM will be updated according
function getFileDetail(fileID, fileName) {
viewModel.fileViewModel.update(fileID, fileName);
}
Add the update function in FileViewModel
function FileViewModel() {
// rest of the code
self.update = function(fileID, fileName) {
var file = ko.utils.arrayFirst(self.Files(), function(file) {
return file.ID == fileID;
});
file.fileName(fileName); // this will change and the UI will be updated according
};
}
Note: Please notice that you have a default item in Files that will not be changed with update function because properties are not observable
self.Files = ko.observableArray([{ ID: 1, fileName: "", removeFile: function (file) { self.Files.remove(file); }}]);
You can solve this by making them observable (i.e. ID: observable(1)) or you can create a new FileViewModel().
Note: The viewModel must be accesible in the function (i.e. global instance), otherwise will be undefined.
It looks to me that setting a field's value via the DOM does not interact with knockout. If you are setting its value using .value, the observable will not be updated. You should be updating the observable.
I wrote a little Fiddle to demonstrate. Every 2 seconds, it sets the input's value via the DOM, but the bound observable only changes when you type something.
http://jsfiddle.net/qcv01h2e/
var viewModel = (function () {
return {
fv: ko.observable().extend({notify:'always'})
};
}());
ko.applyBindings(viewModel);
setInterval(function () {
console.debug("Set it");
var f = document.getElementById('field');
f.value = "Hi";
console.debug("fv is", viewModel.fv());
}, 2000);
I came across a similar issue where I need to set a value without user input.
Before doing the click update function I do the required model update. If you have mode operations better to introduce a function in the model.
<input data-bind="click: function(){ isEnabled(true); update() }" />
What I actually did was,
<input data-bind="click: function(){ isEnabled(!isEnabled()); update() }" />
Keep in mind that asynchronous nature of javascript.

How to send ajax request to check session timeout and render relogin message in grails?

I want to display a message to the user saying, "you're logged out re-login please!" when session is timed-out, sending an ajax request each time. If session timer ends i want to send final ajax request displaying above message. But the problem here is i don't know where should i have to keep my ajax and jquery codes and since i don't have much knowledge about ajax request, can anyone explain this process with codes. In siple my requirement is like of what facebook shows on session time out, or when any one tab in case of multiople tabs are logged out. I'm working on grails project.
Do your ajax request like this
$.ajax({
url:url,
type:"POST", // or get
data:parameters,
success: function(data) {
// do procedure if success
}
error : function(xhr, type, error){
// do procedure if fail
// may be send a message to the server side to display a message that shows session timeout
}
});
Handle your session timeout in the error function
I did it myself and this is the js code for it "gracefulSession.js" and call this javascript at the page where you are going to embed your html code..
function checkSessionStatus() {
var lStorage = getLocalStorage();
if (lStorage) {
//lStorage.setItem('poleTime',new Date());
var poleTime = lStorage.getItem("poleTime");
var parsedTime;
try {
parsedTime = new Date(poleTime);
} catch (e) {}
//alert(new Date()-parsedTime)
//alert(new Date())
//alert(parsedTime)
//3900000 = 1H5M
if (parsedTime && (new Date() - parsedTime) < 3900000) {
//alert('NCATCH'+parsedTime);
} else {
//alert('POINT');
poleSessionStatus();
}
}
}
function setlatestPoleTIme() {
//alert("SETTING POLE TIME");
var lStorage = getLocalStorage();
if (lStorage) {
lStorage.setItem('poleTime', new Date());
}
}
function setCheckSessionTimer() {
var lStorage = getLocalStorage();
var isLoggedOut = false;
if (lStorage) {
if (lStorage.getItem('isLoggedOut') == 'true') {
isLoggedOut = true;
}
}
//console.log('checkingIfLoggedOut');
if (!isLoggedOut) {
setTimeout("setCheckSessionTimer();", 5000);
//console.log("NOPT LO");
$('#LoggedoutMessage').hide();
checkSessionStatus();
} else {
setTimeout("setCheckSessionTimer();", 5000);
//console.log("KO");
//alert("You're Logged Out from other tab");
$('#LoggedoutMessage').show();
}
}
function logout() {
//alert("LOGGIN OUT")
var lStorage = getLocalStorage();
if (lStorage) {
lStorage.setItem('isLoggedOut', 'true');
}
}
function resetLoggedOutFLag() {
var lStorage = getLocalStorage();
if (lStorage) {
lStorage.removeItem('isLoggedOut');
}
}
function getLocalStorage() {
var storage, fail, uid;
try {
uid = new Date;
(storage = window.localStorage).setItem(uid, uid);
fail = storage.getItem(uid) != uid;
storage.removeItem(uid);
fail && (storage = false);
} catch (e) {}
return storage
}
Now, HTML code to embed ,
<div id="LoggedoutMessage" style="display:none;position:absolute;background:black;height: 200%;width:100%;top: 0;z-index: 10000;opacity: 0.9;">
<div id="login_box" style="position:fixed;left:38%;top:30%; padding:10px; width: 365px;margin: 0 auto;border: 0px solid #CCC;margin-top: 35px;height: 150px;background: white; border-radius:3px;">
<div id="login_title">
<h1>You have been logged out.</h1>
</div>
<div id="reLogin">
<p>Please login to continue.</p>
<g:link controller="dashBoard" action="index" target="_blank" onclick="logout();">Login</g:link>
</div>
</div>
</div>
Finally where you keep your html,keep this javascript code at top of it embedding script tag:
function poleSessionStatus() {
jQuery.ajax({
type: 'POST',
data: '',
url: '<g:createLink action="ajaxCheckSession" controller="dashBoard"/>',
success: function (data, textStatus) {
//setTimeout ( "checkSession();", 5000);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
$('#LoggedoutMessage').show();
},
complete: function (XMLHttpRequest, textStatus) {
$.unblockUI();
}
});
}

Multiple AJAX functions

I am trying to add an ajax function to a script that does 2 things essentially:
Step 1: Determine if it needs to search for users or create a new one
Step 2: Based on selection 1, it will either go to the selected script (that part works) or call a new function (that part doesn't work, yet). Now, I know the 2nd function itself works perfectly as I called it directly in the anchor tag and had no issues, so it has to be in how I am trying to all it witin the function itself. here's what I have so far:
function changecm(){
var txt = 'Select An Option To Continue:<br>
<input type="radio" name="type" id="type" value="search" style="font-size:22px;"><br>
<input type="radio" name="type" id="type" value="create" style="font-size:22px;">';
$.prompt(txt,{
buttons:{Confirm:true, Cancel:false},
submit: function(v,m,f){
var flag = true;
if (v) { }
return flag;
},
callback: function(v,m,f){
if(v){
var type = f.type;
if(type == 'create'){
$.post('changecm',{type:type},
function(data){
$("div#customer").html(data);
}
);
}
else{
function(changecmnow);
}
}
}
});
}
That's function 1. Here's function 2:
function changecmnow(){
var txt = 'Enter the first name, last name, or telephone number of the customer to limit your results:<br>
<input type="text" name="terms" id="terms" style="font-size:22px; width:400px;">';
$.prompt(txt,{
buttons:{Confirm:true, Cancel:false},
submit: function(v,m,f){
var flag = true;
if (v) { }
return flag;
},
callback: function(v,m,f){
if(v){
var terms = f.terms;
$.post('changecm',{terms:terms},
function(data){
$("div#customer").html(data);
}
);
}
}
});
}
if you just want to invoke the function, why not just
else { changecmnow();
}

Resources