Extjs 3.4 Button fires n times - javascript

I have a button that fires some action such as
someBtn.on('click'm function(..) { ... }
and I did some arithmetic operation inside. I saw calculated numbers and it was wrong, so I used console.log() to track it and the action was being fired multiple times.
More specifically, I have a window, let's say A, that has multiple rows and when I click one of the rows another window, B, pops up and I can type numbers there. When I type numbers and click saves it does some works and hide B windows and I can click other rows on A to alter other rows.
When I click first time, B runs only once, and second time, it runs twice, third, three times ....
I have been looking at my code but I have no idea how this can be happening.
This is my code:
function openCustDeposit(idCustomer) {
require(['view/Deposit'], function (dep) {
var windep= Ext.getCmp('windep');
if (!windep) {
windep= new Ext.Window({
...
items : [dep.initObj],
plain : true,
closable: true,
closeAction :'hide',
...
listeners:{
show:function() {
this.loadMask = new Ext.LoadMask(this.body, {
msg:'Loading. Please wait...'
});
},
destroy:function(){
if(this.mask)delete this.mask;
}
}
});
} else {
....
}
...
windep.show();
...
dep.store.load();
dep.grid.custDepositPostBtn.on('click', function(obj,evt)
{
try {
var str_idCrdtMemo='', str_CRApply='', sumCRApply=0;
var objGrid = grid;
var selRec = objGrid.selModel.getSelected();
....
selRec.set('CustDeposit', sumCRApply);
selRec.set('idPostCheck', str_idCrdtMemo.substr(0,str_idCrdtMemo.length-1));
selRec.set('CustDeposit2', str_CRApply.substr(0,str_CRApply.length-1));
....
closeWin(arCustDeposit.grid); // window close
}
catch (e) { Ext.MessageBox.alert('Error', e); }
finally {
....
}
});
});
}`
Any advice would be very helpful. Thanks

Related

Why does the Leaflet search box dissappear so easily? And how to keep it around?

I've implemented the Leaflet search-box extension.
Whenever I use the mouse to pan around on the map, the search box dissappears immediately and the user has to re-type the entire query every time.
I'd like the search-box and it's query to hang around until the user presses the clear button.
A display:block; is added and removed in order to show/hide the search-box. I'd like to only allow the collapse to happen when the user clears the query, instead of on any mouse movement.
Alternatively I've tried to keep the query around so the user doesn't have to re-type it every time. By catching the collapse event and storing the query. But at that point the box has already been cleaned up and there's no text to remember.
var timeout = {};
var oldQuery = "";
controlSearch.on('search:expanded', function () {
var searchBox = this._input;
searchBox.value = oldQuery;
this._input.onkeyup = function () {
clearInterval(timeout);
timeout = setTimeout(function () {
var query = searchBox.value;
search(query, searchItems, searchResultsLayer);
if (query.length > 0) {
if (!searchResultsShown) {
searchResultsLayer.addTo(map);
}
}
else {
if (searchResultsShown) {
searchResultsLayer.removeFrom(map);
}
}
}, 200);
};
}).on('search:collapsed', function () {
oldQuery = this._input.value;
});
To keep the user input you have to remove :
this.cancel();
From :
collapse: function() {
}
To keep the search box from collapse you can add the option collapsed to false like this example :
map.addControl( new L.Control.Search({
container: 'findbox',
layer: markersLayer,
initial: false,
collapsed: false
}) );
Then when you click on clear you should fire the collapse function and everything should work fine.

Incorrect count with Ext.each decrement loop

In a component (eg tab) where two grids are loaded simultaneously with data, I use the following function to create a global mask which will only be removed when the stores have all been loaded. It usually works well.
testLoadAllStores: function(allStores, component){
var indexStores = 0;
setTimeout(function () {
Ext.each(allStores, function(storeId) {
var store = Ext.getStore(storeId);
if(store){
if(store.isLoading()){
indexStores++
console.log(indexStores);
store.on('load', function() {
indexStores--;
console.log(indexStores);
if (indexStores == 0){
setTimeout(function() {
if(component.isMasked()){
component.unmask();
}
}, 500);
}
});
}
else if(!store.isLoading() && indexStores == 0){
setTimeout(function() {
if(component.isMasked()){
component.unmask();
}
}, 500);
}
}
});
}, 500);
}
In the controller the function is called as follows
var allStores = ['storeOne', 'storeTwo'];
var component = Ext.getBody();
component.mask();
App.util.Util.testLoadAllStores(allStores, component);
However I am having a problem in the following situation: every time a row of the grid is selected two charts are displayed. In this case the function testLoadAllStores is called and only when the charts stores are loaded the unmask is fired.
The problem is that every time I select a row (selectChange event) the indexStores-- gives the following values (it works but countdown is not correct).
//first selection
1
2
1
0
//second selection
1
2
-1
1
-2
0
// third selection
1
2
-3
-1
1
-4
-2
0
You keep your old listeners, and just add new ones on top. This means that each time you load the stores, the old listeners are counting down from zero to below-zero.
To prevent cluttering your stores with listeners, potentially slowing the app down over time, you should mark the listener single, which will remove the listener after it is fired for the first time:
store.on('load', function() {
...
}, this, {
single: true
});
Description here: http://docs.sencha.com/extjs/6.2.1/classic/Ext.Evented.html#method-on--options

How to tell if column resize is done manually vs. automatically with onColumnResized()?

Before ag-grid v11.0, sizeColumnsToFit() fired with an event that did not pass the parameter 'finished=true'. When a user manually resized a column, the event would pass 'finished=true' once the resize drag was complete. This allowed me to distinguish between a manual and automatic column resize.
As of ag-grid v11.0, sizeColumnsToFit() now fires an event with parameter 'finished=true'. Is there any way to distinguish between this automatic resize and a manual user resize?
The ColumnEvent, from which the ColumnResizedEvent is derived has a "source" property that reads "sizeColumnsToFit" or "uiColumnDragged" and even "autosizeColumns" when a you double click the partition.
https://www.ag-grid.com/javascript-grid-events/#properties-and-hierarchy
You should be able to use the source to determine how the event was fired.
myEventHandler(ev: ColumnResizedEvent) {
if (ev.source === 'sizeColumnsToFit') {
do.this;
} else {
do.that;
}
}
when you drag the column manually, source is always "uiColumnDragged"
if (event.source === 'uiColumnDragged') {
// your logic here
}
Code added since 10
colsToFireEventFor.forEach( (column: Column) => {
let event: ColumnResizedEvent = {
type: Events.EVENT_COLUMN_RESIZED,
column: column,
columns: [column],
finished: true,
api: this.gridApi,
columnApi: this.columnApi
};
this.eventService.dispatchEvent(event);
});
You can try to modify comment out finished: true property or just use version 10.0, where this func looks like this:
colsToFireEventFor.forEach( (column: Column) => {
let event = new ColumnChangeEvent(Events.EVENT_COLUMN_RESIZED).withColumn(column);
this.eventService.dispatchEvent(Events.EVENT_COLUMN_RESIZED, event);
});
We can set manually and automatically columns size in the following way
const onColumnResized = useCallback(params => {
if (params.source === 'uiColumnDragged' && params.finished) { // automatically
gridParams.api.sizeColumnsToFit()
} else { // manually
const colId = params.column.colId
const value = columns.map(v => {
if (v.colId === colId) {
v.width = params.column.actualWidth
}
return v
})
if (value) {
setColumns(value)
}
}
}, [ gridParams, setColumns ])

running a series of scripts on a series of opened tabs

I am making an experimental addon that will search for a single term on several different art sites at once.
The addon works by doing the following:
Opens a window asking for user input (the term to search for)
Stores this input
Starts a crude loop process
The loop opens the first tab in the list
The loop attaches runs the first script in the list
(these scripts pretty much all work the same way)
This script is attached as a worker to the tab
The script enters the term into a search box in the tab then submits it
The submit triggers the worker to be destroyed
Steps 4-8 repeat until the list runs out of tabs to open
Note: the number of these steps have nothing to do with the step numbers in the code
Main.js
var data = require("sdk/self").data;
var tabs = require("sdk/tabs");
var journal_entry = require("sdk/panel").Panel({
contentURL: data.url("DeltaLogPanel.html"),
contentScriptFile: data.url("get-text.js")
});
// Creates the button
require("sdk/ui/button/action").ActionButton({
id: "make-post",
label: "Make post",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
var count = 0;
function handleClick(state) {
journal_entry.show();
}
journal_entry.once("show", function() {
journal_entry.port.emit("show");
});
function doit() {
console.log("Step 1, loop "+ count +" started!");;
if(count < sites.urls.length)
{
TabIt(count);
}
else
{
console.log("loop ended");
}
}
function TabIt(x) {
console.log("Step 2, tab "+ count +" is opening!");
tabs.open(sites.urls[count]);
handleTab(count);
}
//this is our cargo. There's normally more stuff in it
var Cargo = {
Title: ""
};
var sites = {
urls: ["https://www.sofurry.com/", "https://inkbunny.net/search.php", "http://www.furaffinity.net/search/", "https://www.weasyl.com/search", "http://www.deviantart.com/"],
scripts: ["searchSF.js", "searchIB.js", "searchFA.js", "searchWS.js", "searchDA.js"]
};
function handleTab(X)
{
console.log("Step 3, tab "+ count +" is processing!");
//I tried tabs.on('load'.. and that didn't fix the problem
tabs.on('ready', function RunPostScript(tab)
{
console.log("The tab is ready");
worker = tab.attach(
{
contentScriptFile: sites.scripts[X],
contentScriptOptions:
{
Cargo
}
});
worker.port.once("myMessage", function handleMyMessage()
{
console.log("Step 4, search "+ count +" is shuttin down!");
//tab.close();
worker.destroy();
});
tabs.removeListener("ready", RunPostScript);
console.log("Step 5, search "+ count +" should be finished!");
count=count+1;
console.log("Ready to do it again?");
doit();
});
}
journal_entry.port.once("cargo-shipping", function (cargo) {
Cargo.Title = cargo.title;
console.log("title: " + Cargo.Title);
//All data from panel should be imported. So panel is now hidden
journal_entry.hide();
//this starts the tab opening process
TabIt(count);
});
DeltaLogPanel.js
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<!-- <link type="text/css" rel="stylesheet" href="DeltaLogPanel.css"/> -->
<style>
#MainPanel
{
width: 180px;
height:180px;
background-color: #ACA1A1;
}
</style>
</head>
<body id="MainPanel">
<div id="simpleOptions">
<textarea id="titleBox" placeholder="Title" rows="1"></textarea>
<button type="button" id="publishButton">Publish</button>
</div>
</body>
</html>
get-text.js
var titleArea = document.getElementById("titleBox");
var finalButton = document.getElementById("publishButton");
//this defines the cargo on the button press then ships it.
finalButton.addEventListener('click', function() {
// Remove the newline.
var cargo = {
title: ""
};
cargo.title = titleArea.value;
self.port.emit("cargo-shipping", cargo);
titleArea.value = "";
}, false);
//focusses on title box when button is pressed
self.port.on("show", function onShow() {
titleArea.focus();
});
searchFA.js
document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
var FAzA = document.querySelector("form#search-form fieldset input#q");
FAzA.value = x;
};
//hits the submit button
var ShipIt = function(){
document.querySelector("form#search-form").submit();
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();
searchIB.js
document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
var IBzA = document.querySelector("#text");
IBzA.value = x;
};
//hits the submit button
var ShipIt = function(){
var x = document.querySelector("body > form:nth-child(12)");
var y =document.querySelector("body > form:nth-child(9)");
if (x !==null || y !==null)
{
if (x !== null)
{
x.submit();
}
else
{
y.submit();
}
}
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();
searchSF.js
document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
var SFzA = document.querySelector("#headersearch");
SFzA.value = x;
};
//hits the submit button
var ShipIt = function(){
document.querySelector(".topbar-nav > form:nth-child(3)").submit();
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();
searchWS.js
document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
var WSzA = document.querySelector("form#search-backup-search input");
WSzA.value = x;
};
//hits the submit button
var ShipIt = function(){
document.querySelector("form#search-backup-search").submit();
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();
searchDA.js
document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
var DAzA = document.querySelector("input.gmbutton2");
DAzA.value = x;
};
//hits the submit button
var ShipIt = function(){
document.querySelector("#search7").submit();
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();
What does work
It stores the user input.
The addon loads all the tabs in the series perfectly.
The first script runs smoothly on the first tab.
When tested separately, each of the scripts runs the search result like they should.
The problem
When everything is finished, not all the tabs display the search results. Several of the search boxes are left blank as if the scripts never ran on those tabs.
The script of the first tab always works, but the scripts on the rest of the tabs have a random chance of not working.
I noticed the console.log("Step 4, search "+ count +" is shuttin down!"); is never executed. I think this means the worker is never destroyed.
That might be causing these issues.
The worker for each script should be destroyed after the script runs the following: self.port.emit("myMessage");
This little segment of code is at the top of every script. But I think the submit process happens too fast for the event listener to be triggered.
Error message
I am getting the following error message for each of the scripts that do not work:
Object
- _errorType = TypeError
- message = FAzA is null
- fileName = resource://gre/modules/commonjs/toolkit/loader.js -> resource:/
/gre/modules/commonjs/sdk/loader/sandbox.js -> resource://jid1-tbpzbqttcoeaag-at
-jetpack/my-addon/data/searchFA.js
- lineNumber = 8
- stack = titlePort#resource://gre/modules/commonjs/toolkit/loader.js -> res
ource://gre/modules/commonjs/sdk/loader/sandbox.js -> resource://jid1- tbpzbqttco
eaag-at-jetpack/my- addon/data/searchFA.js:8:2|Finalize#resource://gre/modules/co
mmonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/loader/sandbox.j
s -> resource://jid1-tbpzbqttcoeaag-at-jetpack/my- addon/data/searchFA.js:15:1|#r
esource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commo
njs/sdk/loader/sandbox.js -> resource://jid1-tbpzbqttcoeaag-at-jetpack/my- addon/
data/searchFA.js:18:1|
- name = TypeError
If only one tab/script is run, this error never happens. This is true for every tab/script combination. So I know they should all work.
My Question to you
How can I destroy the worker of each script after the script runs the submit function?
Your error:
FAzA is null
indicates that the element was not found in the page when using querySelector().
The problem is mostly here:
tabs.on('ready', function RunPostScript(tab) {...});
This monitors all the tabs and creates a race condition when submitting a search. The following happens:
attach ready event listener to tabs waiting for site A
open site A
page A fires ready event, attach script A to site A
remove ready event listener
increment count
end of loop, call next tab
attach ready event listener to tabs waiting for site B
meanwhile, site A submits the search, it loads the result page and fires a ready event
attach script B to site A
site A is now processed as site B, script B can't find the search input, outputs error
remove ready event listener
increment count
end of loop, call next tab
attach ready event listener to tabs waiting for site C
etc
You should monitor events at the tab level. Use once() so you don't have to remove the listener. Something like this:
function TabIt(x) {
tabs.open({
url:sites.urls[x],
onOpen: function(x, tab) {
// fire once
// count is available in this scope, no need to setup as arg again
tab.once('ready', function readyStuff(tab) {
tab.attach({
contentScriptFile: sites.scripts[x],
contentScriptOptions:{Cargo}
});
});
}
});
++count;
doit();
}
You also keep using count instead of x, while it's possible to do so, it would require binding the value because of the scope in which count exists. But since you have x, just use it.
I also advised in comments to use DOMContentLoaded in attached scripts. After reading more on script attachment to tabs, that was bad advice.
Your code could be vastly optimized but let's do one thing at a time.
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
var aDOMWindow = getMostRecentBrowserWindow();
if (aDOMWindow.gBrowser && aDOMWindow.gBrowser.tabContainer) {
var tabs = aDOMWindow.gBrowser.tabContainer.childNodes;
for (var i=0; i<tabs.length; i++) {
// tabs[i].contentWindow.wrappedJSObject.... // not e10s friendly, so to tabs[i].messageManager or something like that
}
}
here is frame scripts which are absolutely easy:
https://github.com/mdn/e10s-example-addons/tree/master/run-script-in-all-pages

function iterates through loop and works once but never again

I'm calling a Titanium modal window to open and then run a function which loops through some data like so;
Window 1:
var win = Ti.UI.createWindow({
url: 'window2.js'
modal: 1
});
win.open();
Window 2: (called from window 1)
win = Ti.UI.currentWindow;
function doLoop() {
Ti.API.info('doLoop fn called');
// I've tracked the issue down to here
var m = 0;
for(var i in list) { m++; }
Ti.API.info(m);
Ti.API.info('finished');
}
win.addEventListener('open', function() {
// list is dynamically generated and passed through successfully from window1.js
doLoop();
});
doLoop() is called successfully each time and list is called each time successfully.
The first time run it works perfectly. The second(any that isn't first) time run it takes time to pause and run the the loop but m is never incremented? After the pause for the loop outputs 'finished'.
Any ideas?
function buildMediaItemsSelectionTable() {
var mediaCount = 0, i;
for(i in mediaItemsSelectionList[0]) { mediaCount++; }
for(i=0,l=mediaCount;i<l;i++) {
addMediaItemsSelectionSongsRow(i);
}
}
There are several issues I see here.
First, the problems with your buildMediaItemsSelectionTable() function
Your for..in loop might catch object properties you don't
There's no need for the double loop
Here's those modifications in place
function buildMediaItemsSelectionTable()
{
var i = 0, p;
for ( p in mediaItemsSelectionList[0] )
{
if ( mediaItemsSelectionList[0].hasOwnProperty( p ) )
{
addMediaItemsSelectionSongsRow( i++ );
}
}
}
The other issue is one I'm having to guess at since you didn't provide enough code. I'm assuming that you're passing list to the modal with Titanium's variable forwarding. Perhaps something like this?
var win = Ti.UI.createWindow({
url: 'window2.js'
, modal: 1
, list: [1,2,3]
});
And something has to repeatedly open the modal, right? Maybe a button
var button = Ti.UI.createButton( {title: 'Modal'} );
Ti.UI.currentWindow.add( button );
button.addEventListener( 'click', function()
{
win.open();
});
But according to your description, list changes so let's make a random list generator and plug it in to our page so the entire thing looks like this
var win = Ti.UI.createWindow({
url: 'window2.js'
, modal: 1
, list: randomList()
});
var button = Ti.UI.createButton( {title: 'Modal'} );
Ti.UI.currentWindow.add( button );
button.addEventListener( 'click', function()
{
win.open();
});
function randomList()
{
// Random return an array with 3, 5, or 7 items
return [[1,2,3],[1,2,3,4,5],[1,2,3,4,5,6,7]][Math.floor(Math.random()*2)];
}
What's wrong here? randomList() is only called once, regardless of how many times you open the modal. Even if window1 is part of a nav or tab group, the code that creates the modal window doesn't re-execute under any circumstances.
If you want a new list to be forwarded to the modal every time, you'll have to generate it fresh every time
button.addEventListener( 'click', function()
{
win.list = randomList();
win.open();
});
Looks like your '}' is in the wrong place. Right now you have a loop with a single (likely unintended) side effect - m counts up to the length of the list and then there is a call to API.info with the length of list.
You probably want :
function doLoop() {
Ti.API.info('doLoop fn called');
// I've tracked the issue down to here
var m = 0;
for(var i in list) {
m++;
Ti.API.info(m);
Ti.API.info('finished');
}
}

Categories