Asynchronous paradox BackboneJS - javascript

What I am trying
On Save or Delete of my form a simple notification is poped-up to user
What I have done
events :{
'#save-button click' : 'onSaveBUttonClick',
'#delete-button click' : 'onDeleteButtonClick'
};
onDeleteButtonClick = function(){
//popup appears to confirm delete
this.model.on('sync',function(model){
model.off('sync');
alert("project deleted");
},this);
this.model.destroy();
}
onSaveBUttonClick = function(){
//popup appears to confirm delete
this.model.on('sync',function(){
model.off('sync');
alert("project Saved");
},this);
this.model.save();
}
The problem
I click on the delete button and say , select , cancel. Here the model.on('sync') is bound to the model.
Now when I click save , and confirm , the model.on('sync') is called twice (one bound by delete button and one bound by save button).
So I am getting 2 pop-ups Project deleted first and project saved after it.
How can I avoid this?

You can use the success options in model.save and model.destroy
destroy model.destroy([options])
Destroys the model on the server by delegating an HTTP DELETE request to Backbone.sync. Returns
a jqXHR object, or false if the model isNew. Accepts success and error
callbacks in the options hash.
save model.save([attributes], [options])
[...]
save accepts success and error callbacks in the options hash.
Your methods could look like
onDeleteButtonClick = function(){
this.model.destroy({
success: function() {
alert("project deleted");
}
});
}
onSaveBUttonClick = function(){
this.model.save(null, {
success: function() {
alert("project saved");
}
});
}

Related

backbone backgrid: how to avoid double change event

I have a backbone/backgrid editable grid, and everytime I edit a cell the "change" event is fired up twice and I end up making two separate PUT requests.
I know that this happens because the "change" event fires once when I edit it in the cell, and another when the data comes back from the server; and that the behaviour could be avoided by passing {wait: true} to the save method, but I don't know where I need to overload it.
My model declaration is like this:
var Redirects = Backbone.Model.extend({
urlRoot: '/global/redirects',
initialize: function () {
Backbone.Model.prototype.initialize.apply(this, arguments);
this.on("change", function (model, options) {
if (options && options.save === false) return;
model.save({
error: alertMe
});
});
this.on('fetch request', function (e) {
loadingOn(e);
});
this.on('sync error', function (e) {
loadingOff(e);
});
this.on('error', function(e, resp){
alertMe(e, resp);
});
}
});
You shouldn't receive a 2nd change event when a Model is synced from the server, regardless of the wait option.
The problem in your save call is that you haven't specified the attributes hash, i.e. the first parameter. If you don't have any attributes to modify, which considering you are firing save for a change event, that's probably the case, you will need the following:
this.on("change", function (model, options) {
if (options && options.save === false) return;
model.save(null, {error: alertMe});
});
What was actually happening was that you were setting error as an attribute on the model, and that triggered the change event.

Handling multithreadiing issue in javascript/jquery?

$(".getDetails").click(function() {
// some stuff like fetching response from server
})
when user clicks getDetails button on UI multiple times within fraction of second , jquery generates two calls for click function and my logic fails.
I think solution to this will be to disable the button on first click itself(so that use can't click multiple times). Once i get the response or just before returning
from click method i make it enable. Is there any better solution ?
If no, how can i make button disable as soon as user click button first time. I think it needs to be done before calling click method or some where in html element ?
Java provides synchronized keyword so that only one thread enters at time inside method , i am not sure is similar thing exist in javascript or not ?
Assuming the click handler executes an AJAX request you can set the button as disabled before making the request, then enable it again once the request completes. Try this:
$(".getDetails").click(function(){}
var $btn = $(this).prop('disabled', true);
$.ajax({
url: '/foo'
success: function() {
console.log('It worked!');
},
error: function() {
console.log('It failed!');
},
complete: function() {
$btn.prop('disabled', false);
}
});
});
you can try unbinding click event and after ajax call again bind click to that class
$(".getDetails").click(function(){}
$(".getDetails").unbind('click');
// some stuff like fetching response from server
)
You can use simple flag to prevent firing your logic multiple times:
var flag = true
$(".getDetails").click(function() {
if (flag) {
flag = false;
//your logic...
//when your code ends (in after-AJAX callback for example)
flag = true;
}
});
$(".getDetails").click(function(e){
var $target = $(e.currentTarget);
// assuming the click listener is on the button
$target.prop('disabled',true);
// request, stuff...and when done:
$target.prop('disabled',false);
})
try Prevent Default and return false to avoid any other event propagation
This is solution is like semaphore or monitor
var progress = false;
$(".getDetails").on('click', function(e) {
if(!progress){
progress = true;
// some stuff like fetching response from server
//also after sucessfull fetch make true to false again
}else{
console.log('something in progress');
}
e.preventDefault();
return false;
})
This should make sure that your button will not fire the async request twice, until you have a response.
function doAjaxReq() {
/*
Add your ajax operation here
as a return value of doAjaxReq
like so:
return $.ajax({
url: '/foo',
type: 'POST',
data: data
})
Since i can't use ajax here let's smilulate
it useing a promise.
*/
promise = new Promise(function(res, rej) {
setTimeout(function(){
res({foo: "bar"});
}, 1000)
})
return promise;
}
/*
Inside here you add the click handlder
only once use `elem.one('click'...`
*/
function addClickHandler(elem) {
elem.one('click', function() {
// do your ajax request and when its
// done run `addClickHanlder` again
// i'm using `.then` because of the promise,
// you should be using `.done`.
doAjaxReq().then(function(data) {
console.log(data);
addClickHandler(elem);
});
})
}
addClickHandler($(".getDetails"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="getDetails">Get Details</button>

Limit ajaxStart function to only 1 of 2 ajax functions

I have a loading.gif that launches each time the user makes an AJAX powered search. However, I've got some search fields that automatically show suggestions while the user types, also powered by AJAX.
Now my loading.gif appears on the user search as well as the search suggestions while typing. How do I limit my function that shows the loading.gif to only show when it's a user AJAX search and not a search-suggestion-while-typing AJAX search?
This is my function:
$(document).ajaxStart(function () {
$(".se-pre-con").fadeIn("fast");
}).ajaxStop(function () {
$(".se-pre-con").fadeOut("fast");
});
how about bind it with condition like if user is still on the search input then dont show the loading.gif else if the user is out of the search input or first contact on the search input then show the loading.gif (refer below)
first the global variable
var input_focus = false;
and then when the specified input is on focus
$("#specified_input").focus(function(){
//set the variable named 'input_focus' to true to reject the showing of the loader (loading.gif) or hide it.
input_focus = true;
}).blur(function(){
//when the specified input lose it focus then set the variable 'input_focus' to false so that the loader (loading.gif) is allowed to show
input_focus = false;
});
$.ajax({
url : 'my-url',
type : 'post',
data : {},
beforeSend : function(){
//check if input is on focus
if(input_focus !== true){
//show the loading.gif, assume that #loader
$("#loader").show();
}else{
//hide the loading.gif, assume that #loader
$("#loader").hide();
}
},
complete : function(){
//when the ajax request is complete
},
success : function(response){
//the response function
}
});
I'd tackle it by either of the following:
1) Add a global variable such as showLoadingAnimation and set it to true or false depending on the need. Within your ajaxStart and ajaxStop do the following:
$(document).ajaxStart(function () {
if (showLoadingAnimation) $(".se-pre-con").fadeIn("fast");
}).ajaxStop(function () {
if (showLoadingAnimation) $(".se-pre-con").fadeOut("fast");
});
2) Instead of changing the jQuery global settings, Wrap the jQuery method with your own method:
//only listen to ajaxStop event so that we can hide the animation
$(document).ajaxStop(function () {
$(".se-pre-con").fadeOut("fast");
});
function myAjax(params, showAnimation) {
if (showAnimation) $(".se-pre-con").fadeIn("fast");
$.ajax(params);
}
//in your code you instead of calling $.ajax({...}) simply use `myAjax({...})`
Hope this helps.

How to reload json data in acitree

I would like to know whether there is any possible way to refresh an aciTree instance from a json object received from the server.
Let's assume I have an html input field.
The user types something and clicks the submit button.
This input is used to get a new version of the json tree model from the server through an ajax call.
That works fine. However, when I type again a new value in the input field and submit the aciTree does not reflect the new values. It still displays the old json object data.
Here is my code.
User Name: <input type="input" id="name" name="name">
<input type="submit" value="search" id="call" >
<script type="text/javascript">
$(document).ready(function(){
// Makes the ajax call and fetches the json for the resource tree.
$('#call').click(function(){
$("#tree").aciTree({
ajax: {
url: 'treeview/jsp/testplansjson.jsp?sname='+document.getElementById("name").value+',
}
});
});
// Refreshing the tree view - Destroy and recreate
$('#call').click(function(){
var api = $('#tree').aciTree('api');
api.unload(null, {
success: function() {
this.ajaxLoad(null);
// Triggering the click handler of the Get Tree View button.
// This will make the ajax call again and bind the tree...
$('#call').trigger('click');
}
});
});
// ACI Tree - event handler.
$('#tree').on('acitree', function(event, aciApi, item, eventName, opt) {
switch (eventName) {
case 'focused':
case 'selected' :
// Fired when an item in the tree is selected.
if(item) {
$currStatus.text('Selected - ' + item.context.innerText);
}
}
});
});
</script>
<div id="tree" class="aciTree aciTreeNoBranches aciTreeFullRow" style="width:100%;height:auto;margin:0;padding:0;border-left:0;border-right:0"></div>
Please let me know whether there is any way to achieve this.
$(_selector_).aciTree(_options_) call will init the tree view just once (using the provided options). Nothing will happen if you call it twice. To be able to init the tree view with other options, you'll need to destroy it first.
In your case, you need just to update the tree view ajax.url option. First unload the tree, then reload it from the new url.
To update one of the aciTree options at runtime, use the option method. Note that you can use the dot notation to reach deep level properties:
api.option('ajax.url', '_your_new_url_');
Then you can call unload/ajaxLoad (as in your example).
<script type="text/javascript">
$(document).ready(function(){
// Makes the ajax call and fetches the json for the resource tree.
$('#call').click(function(){
$("#tree").aciTree({
ajax : {
url: 'treeview/jsp/testplansjson.jsp?sname='+document.getElementById("name").value+',
}
});
});
// Refreshing the tree view - Destroy and recreate
$('#call').click(function(){
var api = $('#tree').aciTree('api');
api.unload(null, {
success: function() {
this.ajaxLoad(null);
// Triggering the click handler of the Get Tree View button.
// This will make the ajax call again and bind the tree...
$('#call').trigger('click');
}
});
});
// ACI Tree - event handler.
$('#tree').on('acitree', function(event, aciApi, item, eventName, opt) {
switch (eventName) {
case 'focused':
case 'selected' :
// Fired when an item in the tree is selected.
if(item) {
$currStatus.text('Selected - ' + item.context.innerText);
}
}
});
});
</script>
<div id="tree" class="aciTree aciTreeNoBranches aciTreeFullRow" style="width:100%;height:auto;margin:0;padding:0;border-left:0;border-right:0"></div>

Backbone.View.model.on('change:...') trigged only once

I'm doing ajax file upload for post via modal window with preview in post. Every post has it's own model and a view. Modal window is also a separate view, binded to existing DOM element.
When Attach button in post view is clicked, I call .open() from modal view, passing post model to modal view as settings:
POST VIEW:
======================
ModalAttach.open({
postModel : this.model
});
When file in modal view is uploaded, I add server response to passed Post model to render it later in post itself as a preview:
MODAL VIEW:
======================
// file upload success
success: function(data) {
// if it's first call, set []
var imagesUploaded = self.postModel.get('images_uploaded') || [];
// add server response to array
imagesUploaded.push(data);
// rewrite current model array to new array
self.postModel.set({ 'images_uploaded' : imagesUploaded });
}
To render preview in post (before real submitting), I've got a function
POST VIEW:
======================
renderUploadedImages: function () {
var self = this;
this.$uploadedImagesWrapper = this.$('.b-uploaded__images');
if (this.model.get('images_uploaded')) {
this.$uploadedImagesWrapper.empty();
this.model.get('images_uploaded').forEach(function (uploadedImage) {
self.$uploadedImagesWrapper.append(
uploadedImageTemplate({
'source': uploadedImage.source
})
)
});
}
}
And to trigger image render, I bind a listner to track when model.images_uploaded is changed by modal view:
POST VIEW:
======================
initialize: function () {
this.addEvents();
this.renderUploadedImages();
},
addEvents: function () {
var self = this;
this.model.on('change:images_uploaded', function () {
self.renderUploadedImages();
})
},
The problem is renderUploadedImages() in Post view is trigged only once, at first upload. Other changes are not caught (when postModel.get('images_uploaded').length becomes 2,3,etc..). What I am doing wrong?
Thanks.
When you do like this :
var imagesUploaded = self.postModel.get('images_uploaded') || [];
// add server response to array
imagesUploaded.push(data);
// rewrite current model array to new array
self.postModel.set({ 'images_uploaded' : imagesUploaded });
The first time, if you check self.postModel.get('images_uploaded') you will find it undefined, that's why when you set it the event change:images_uploaded is triggered.
But the second time you call the success method you don't change the the model attribute, it's always pointing to the same object (array), you just change the array value.
Here's an example

Categories

Resources