Button being called multiple times - javascript

I have a search page and an edit page. I search for a user and then when I get the results, I'm able to edit the user. I'm using CanJS and I've defined routes for each page.
, 'search route': function (data) {
new FormUserQuery('#formArea', {});
}
}
, 'edit/:id route': function (data) {
new FormUser('#formArea', {}).renderView(+data.id);
}
}
In FormUser I have a click event for the saveButton. If I search a user and then press the edit button, change something and save the changes, it works fine. But if, after saving, I go back to the search page and do the same things as I did before, the save button is being called twice.
I have no idea why this is happenning. What am I doing wrong?
Edit
I made it work. Whenever I clicked on a new edit button, somehow the view was being placed on top of another, it wasn't replacing the old one.
So I tried this and it worked:
, 'search route': function (data) {
if (typeof self.form === 'undefined')
{
self.form = new MegaControl.FormUserQuery('#formArea', {});
}
else {
self.form.destroy();
self.form = new MegaControl.FormUserQuery('#formArea', {});
}
}
, 'edit/:id route': function (data) {
if (typeof self.form === 'undefined') {
self.form = new MegaControl.FormUser('#formArea', {})
self.form.renderView(+data.id);
}
else {
self.form.destroy();
self.form = new MegaControl.FormUser('#formArea', {});
self.form.renderView(+data.id);
}
}

You shouldn't need to call destroy manually as you are doing. The issue is that you are using the same DOM element for both views (#formArea).
Can will call destroy automatically for you when an element is removed from the DOM, so that is the step that is missing.
So maybe instead of reusing the same element, you could append an element first and pass that newly created element to the control. Something like:
'search route': function (data) {
$('#formArea').empty(); // this will remove previous elements and trigger destroy automatically
var view = $('<div>');
$('#formArea').append(view);
new FormUserQuery(view, {});
}

Related

Multiple events fired in jquery function

I have html grid table consisting of comment link in each row.Clicking on any one opens a bootstrap modal with textbox and save button.So I wrote a library consisting of functions related to that comment system.Below is basic code.
HTML :
<td><a class="addComment" data-notedate="somevalue" data-toggle='modal' href='#addnotesdiv' data-oprid="somevalue" data-soid="somevalue" data-type="1"><i class="fa fa-comments-o fa-2"></i></a></td> ..... n
JS :
var Inventory={};
Inventory.notes={
defaults:{
type:'1',
soid:0,
operator_id:0,
date:'',
target:'div#addnotesdiv',
},
init:function()
{
var self=this;
$('div#addnotesdiv').on('show.bs.modal',function(e){
self.getandsetdefaults(e);
self.setmodalelements(e);
self.getNotes();
self.addnote();
self.activaterefresh();
});
},
getandsetdefaults:function(e)
{
this.defaults.soid = $(e.relatedTarget).data('soid');
this.defaults.operator_id=$(e.relatedTarget).data('oprid');
this.defaults.type=$(e.relatedTarget).data('type');
this.defaults.date=$(e.relatedTarget).data('notedate');
},
setmodalelements:function(e)
{
$(e.currentTarget).find('#notesthread').empty();
$(e.currentTarget).find('input#inpnotesoid').val(this.defaults.soid);
$(e.currentTarget).find('input#inpnoteoprid').val(this.defaults.operator_id);
$(e.currentTarget).find('input#inpnotetype').val(this.defaults.type);
},
addnote:function()
{
var self=this;
$('button#btnaddnote').on('click',function(){
var message=$(self.defaults.target).find('textarea#addnotemsg').val();
var soid=$(self.defaults.target).find('input[type=hidden][id=inpnotesoid]').val();
var note_date=$(self.defaults.target).find('input#addnotedate').val();
var oprid=$(self.defaults.target).find('input[type=hidden][id=inpnoteoprid]').val();
var type=$(self.defaults.target).find('input[type=hidden][id=inpnotetype]').val();
if(message=="" || soid=="" || note_date=="")
{
alert("Fill all details");
return;
}
var savenote=$.post(HOST+'notes/save',{message:message,soid:soid,note_date:note_date,type:type,operator_id:oprid});
savenote.done(function(res){
res=$.parseJSON(res);
if(res.status && res.error){
alert(res.message);
return;
}
if(res.status && res.type)
{
$('div#addnotemsg').showSuccess("Done").done(function(){self.getNotes();});
$('div#addnotesdiv').find('textarea#addnotemsg').val('');
}
else
{
$('div#addnotemsg').showFailure("Error");
}
});
});
},
getNotes:function()
{
$('button#btnrefreshcomments i').addClass('glyphicon-refresh-animate');
var getnotes=$.getJSON(HOST,{soid:this.defaults.soid,type:this.defaults.type,note_date:this.defaults.date,operator_id:this.defaults.operator_id});
getnotes.done(function(res){
if(res.status && res.data.length)
{
--somecode---
}
});
},
activaterefresh:function(){
var self=this;
$(document).on('click','#btnrefreshcomments',function(){
$('#notesthread').empty();
self.getNotes();
return false;
});
return false;
}
}
In Order to activate this functionality on that page I wrote
Inventory.notes.init();
Above code works perfectly when I open modal once but when I close that same modal and open it again but by clicking on different link all events are fired twice,thrice and so on.Number of events fired is equal to number of times modal opened on that page.
Is there any thing wrong in code Or any other way to perform this same task.
I know this is not a plugin all I wanted was to store all functionality related to comment system under one roof as library.
every time you open the modal box, it triggered show.bs.modal event, then all methods was exec again, including the event bindings. e.g. event bind in [addnote]
$('div#addnotesdiv').on('show.bs.modal',function(e){
self.getandsetdefaults(e);
self.setmodalelements(e);
self.getNotes();
self.addnote();
self.activaterefresh();
});
Problem was whenever modal was shown getNotes,addnote,activatereferesh functions were called but when the modal was reopened again this functions are called again so thats twice and so on.Putting it in more simpler way is there were multiple listeners attached to single element without destroying previous one because my init function was called many times.
At last there were two solutions in both I need to unbind events or attach them only once.Got idea from here
1) Modified Init function with below code and added one unbind listener function
init:function(selector)
{
var self=this;
$(self.defaults.target).on('show.bs.modal',function(e){
self.getandsetdefaults(e);
self.setmodalelements(e);
self.getNotes();
self.addnote();
self.activaterefresh();
});
$(self.defaults.target).on('hide.bs.modal',function(e){
self.unbindlistners();
});
}
unbindlistners:function()
{
var self=this;
$('#btnrefreshcomments').unbind('click');
$('button#btnaddnote').unbind('click');
return false;
}
}
2) Place event binding function outside show.bs.modal
init:function(selector)
{
var self=this;
$(self.defaults.target).on('show.bs.modal',function(e){
self.getandsetdefaults(e);
self.setmodalelements(e);
});
self.getNotes();
self.addnote();
self.activaterefresh();
}
There is small catch in second solution that is when first time my DOM is loaded function getNotes function is called with default values.

dojo for content from ajax call

The case:
I use dojo to request a page and load it into a div ( view ).
The problem:
The content that gets loaded into a form contains a dojo form and relevant objects, textbox, etc... how can I controller these widgets? I believe the current way I am working around the issue is sloppy and could be more refined.
Comments are in the code to help explain the issue I have. Please let me know your thoughts.
function (parser, domAttr, util, ready, dom, on, request, domStyle, registry, TextBox) {
//This prepares the doc main html page We are looking for a click of a menu option to load in thats pages pages content
ready(function () {
//Look for click of menu option
on(dom.byId('steps'), "a:click", function(e) {
event.preventDefault();
//Get the div we are going to load the page into
var view = domAttr.get(this, "data-view");
// function that loads the page contents
load_page(view);
});
});
function load_page(view) {
//First I see if this page already has widgets and destroy them
//We do this so users can toggle between menu items
// If we do not we get id already registered
var widgets = dojo.query("[widgetId]", dom.byId('apply-view')).map(dijit.byNode);
dojo.forEach(widgets, function(w){
w.destroyRecursive();
});
//get the html page we are going to user for the menu item
request.post("/apply_steps/"+view, {
data: {
id: 2
}
}).then(
function(response){
//Add the content and parse the page
var parentNode = dom.byId('apply-view');
parentNode.innerHTML = response;
parser.parse(parentNode);
//This is where it is sloppy
//What I would prefer is to load a new js file the controlls the content that was just loaded
//What happens now is I create a traffic director to tell the code what main function to use
controller_director(view);
},
function(error){
util.myAlert(0, 'Page not found', 'system-alert');
});
}
function controller_director(view) {
//based on the view switch the function
switch(view) {
case 'screening_questions':
screening_questions();
break;
}
}
function screening_questions() {
//Now we are controlling the page and its widgets
// How would I get this info into a seperate js file that i would load along with the ajax call??
ready(function () {
on(dom.byId('loginForm'), "submit", function(e) {
event.preventDefault();
var formLogin = registry.byId('loginForm');
authenticate();
});
});
this.authenticate = function() {
var formLogin = registry.byId('loginForm');
if (formLogin.validate()) return;
}
}
});

Need to clear a function memory. jQuery running function too many times

Initially, I had a problem that a click event was firing multiple times, but I have managed to overcome that with a probably over use of unbind() and one() as you'll see in my code below!
What I have here is some code which opens up a universally usable Modal window which I use for various things, including, in some cases a password form.
I don't think you need the HTML so I won't post that.
When a button, or an action causes the window to be required, I call the function like this:
showModalAlert(type, theWidth, theHeight, title, html, confirmThis, denyThis)
The first three variables determine how the window will look, title and html determine the content and confirmThis and denyThis are functions set immediately prior to calling this function and determine what the action should be if this is a confirm window and the confirm or deny buttons are press.
In the case of a security window, the confirm button is replace by a "sign it" button which submits a simple password form and returns a User Id from database. If a User Id is successfully returned, the script programatically presses the confirm button and in turn runs it's function as per the call to the inital opening of the modal window.
My problem is that if an incorrect password is entered, or a user cancels the window and then later without refreshing the browser window, re-enters the password correctly, the confirmThis() function is performed twice (or as many times as the incorrect password/cancel action was performed).
So, clearly, what it is doing is "remembering" the confirmThis function each time.
As I said, initially, the password success function was clicking confirmIt twice, copious use of one() has fixed this, it is now definitely only clicking confirmIt once, but it is still performing the function multiple time.
How can I clear this function and ensure it is only performed once?
The function from which I am calling the modal window looks like this:
$('#saveDelivery').click(function () {
function confirmIt() {
formData = (JSON.stringify($('#delDetail').serializeObject()));
saveData(formData);
$('#saveDelivery').removeClass('centreLoader');
};
showModalAlert('security', '300px', '185px', 'Security!', 'You need to "Sign" this action.', confirmIt, '');
});
It's simply a click on the saveDelivery element, the confirmThis function is declared at this point and submits an AJAX form
the actual showModalAlert function is below:
function showModalAlert(type, theWidth, theHeight, title, html, confirmThis, denyThis) {
// stuff that opens the alert window \\
if (confirmThis == '') {
$('#confirmIt').one('click', function () { $('#closeAlert').one('click').click(); });
} else {
$('#confirmIt').one('click', function () { confirmThis(); $('#closeAlert').one('click').click(); });
};
if (denyThis == '') {
$('#denyIt').one('click', function () { $('#closeAlert').one('click').click(); $('#signIt').unbind(); });
} else {
$('#denyIt').one('click', function () { denyThis(); $('#closeAlert').one('click').click(); $('#signIt').unbind(); });
};
if (type == "confirm") {
$('.closeAlert, .signItForm').hide();
};
if (type == "alert") {
$('.alertConfirm, .signItForm').hide();
};
if (type == "fixedAlert") {
$('.closeAlert, .alertConfirm, .signItForm').hide();
};
if (type == "security") {
$('.signItForm').show();
$('.closeAlert').hide();
$('#confirmIt').hide();
$('#signIt').unbind().fadeTo('fast',1);
};
};
$('#signIt').live('click', function () {
var formData = (JSON.stringify($('.secureSign').serializeObject()));
var signitPwd = $('#signItpwd').val();
var jsonURL = "/jsonout/getdata.aspx?sql=SELECT id, password FROM users WHERE password ='" + signitPwd + "' LIMIT 1&output=json&usedb=new&labelName=any&fileName=";
$.getJSON(jsonURL, function (data) {
if (data.length > 0) {
$('.savingUserID').val(data[0].id);
$('#confirmIt').one('click').click();
$('#signIt').fadeTo('fast', 0);
$('#confirmIt').show();
} else {
$('#signIt').fadeTo('fast', 0);
$('#confirmIt').one('click').show();
$('.closeAlert').show();
$('.alertConfirm, .signItForm').hide();
$('#alertTitle').html("Error!");
$('#alertContent').css({ 'text-align': 'center' }).html("Password Denied");
};
});
});
From my understanding of $.one, it merely runs the event ONCE. If you bind it twice to the event, it will run twice instantaneously, but no more.
Example: http://jsfiddle.net/qCwMH/ (click the button, and it will run the event 4 times).
Each time you click saveDelivery, you are infact, binding another $.one event to #confirmIt.
What you could do is unbind your events from confirmIt and denyIt at the start of the modal function (i.e. $('#confirmIt, #denyIt').unbind('click');, and then you will assign them fresh each time that function is called, rather than building on top of them. Not ideal, as binding/unbinding uses more resources than other options, but just give that a try to start with perhaps?

CKEditor: HowTo destroy instance on blur?

I have a single html page with multiple div elements on it. Each time a user clicks on on a div, it is replaced with an CKEditor on the fly:
$('.editable').click(function() {
editor = CKEDITOR.replace(this);
});
Now, if the CKEditor instance loses its focus (blur event), I need to post the content to a separate script via ajax (if something changed) and destroy this instance:
$('.editable').click(function() {
editor = CKEDITOR.replace(this);
editor.on('blur', function(e)
{
if (e.editor.checkDirty())
// get data with e.editor.getData() and do some ajax magic
e.editor.destroy();
});
});
But this example won't work because, I don't know why, destory() will be called before checkDirty(). How can I get this working?
How about if you put the destroy() inside the if() statement? You could have an else clause that invokes destroy if nothing has changed. If something has changed, you can invoke destroy() within the if clause once the data has been transfered.
$('.editable').click(function() {
editor = CKEDITOR.replace(this);
editor.on('blur', function(e)
{
if (e.editor.checkDirty()) {
// get data with e.editor.getData() and do some ajax magic
if ( dataTransferComplete ) {
e.editor.destroy();
}
} else {
e.editor.destroy();
}
});
});
Or you could check a variable before invoking destroy(). Set that variable to true after the data transfer has been completed and in the else clause, that way destroy() won't be invoked until you've checked for changes and transfered any updated data.
$('.editable').click(function() {
editor = CKEDITOR.replace(this);
editor.on('blur', function(e)
{
var okToDestroy = false;
if (e.editor.checkDirty()) {
// get data with e.editor.getData() and do some ajax magic
okToDestroy = true;
} else {
okToDestroy = true;
}
if (okToDestroy )
e.editor.destroy();
});
});
This is an outline, I haven't tested the code, but if shows the concept.
Be Well,
Joe

how to wait for loading data into the store

There are menu button ("clients"), tree panel with clients list (sorted by name) and viewer with selected client details. There is also selectionchange action..
My task - on button click switch to client view and select and load details for first client every time button has been clicked. My problem - store is not loaded, how waiting until ext js will autoload data to the store?
my controller code:
me.control({
'#nav-client': {
click: me.onNavClientClick
},
...
'clientlist': {
// load: me.selectClient,
selectionchange: me.showClient
}
});
onNavClientClick: function(view, records) {
var me = this,
content = Ext.getCmp("app-content");
content.removeAll();
content.insert(0, [{xtype: 'clientcomplex'}]);
var first = me.getClientsStore().first();
if (first) {
Ext.getCmp("clientList").getSelectionModel().select(me.getClientsListStore().getNodeById(first.get('clientId')));
}
},
...
Two main questions:
is it good solution in my case? (to select first client in tree panel)
var first = me.getClientsStore().first();
// i use another store to get first record because of i dont know how to get first record (on root level) in TreeStore
...
Ext.getCmp("clientList").getSelectionModel().select(me.getClientsListStore().getNodeById(first.get('clientId')));
i know this code works ok in case of "load: me.selectClient," (but only once),
if i place this code on button click - i see error
Uncaught TypeError: Cannot read property 'id' of undefined
because of me.getClientsListStore() is not loaded.. so how to check loading status of this store and wait some until this store will be completely autoloaded?..
Thank you!
You can listen the store 'load' event. Like this:
...
onNavClientClick: function(view, records) {
var me = this;
// if the store isn't loaded, call load method and defer the 'client view' creation
if (me.getClientsStore.getCount() <= 0) {
me.getClientsStore.on('load', me.onClientsStoreLoad, me, { single : true});
me.getClientsStore.load();
}
else {
me.onClientsStoreLoad();
}
},
onClientsStoreLoad : function () {
var me = this,
content = Ext.getCmp("app-content");
content.removeAll();
content.insert(0, [{xtype: 'clientcomplex'}]);
var first = me.getClientsStore().first();
if (first) {
Ext.getCmp("clientList").getSelectionModel().select(me.getClientsListStore().getNodeById(first.get('clientId')));
}
},
...

Categories

Resources