Incorrect count with Ext.each decrement loop - javascript

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

Related

cannot set innerHTML property of null on Qualtrics

I am using Qualtrics to make a survey, and I need to do a bit of JS to make a timer. Unfortunately, I'm constantly running into "cannot set innerHTML property of null" for element "s5".
I've read the other thread about this issue (albeit the OP doesn't seem to be using qualtrics), and thought that perhaps changing "Qualtrics.SurveyEngine.addOnload" to "Qualtrics.SurveyEngine.addReady" might do the trick, but it doesn't, and I've already tried changing the id's quite a few times to no avail. Could someone help me find where my error is?
I got marked previously for the same question (as something that's already been answered), but that thread didn't help me at all. I've tried ready() as shown in the commented out section in the first code snippet, but that only gave me a "startThinkingTimer is not defined" error. When I tried it the second way in the second code snippet, I didn't get any errors, but my timer wasn't visible/working at all either. I can't move script or use defer b/c Qualtrics does not have all the HTML/CSS/JS in one file, but has different sections for them and honestly I'm not sure how they connect the different files. Regarding using .on(), I'm not sure which event to use here, and would really like some help.
I've tried replacing all the document.getElementById for element "s5" with something like this:
$("s5").innerHTML="10";
but this doesn't work, either.
(Should I try to move the html code inside the JS portion (esp. the div timeShower part)? I'm not too sure how to do that though, so if someone could help me do that, that'd be awesome.)
window.thinkingTimer_;
window.typingTimer_;
Qualtrics.SurveyEngine.addOnload(function(){
that = this;
var thinkingTimeLimit = 15;
var typingTimeLimit = 10;
jQuery(".InputText").hide();
$('NextButton').hide();
document.getElementById("instructions5").innerHTML = "You have 15 seconds to think about the prompt and come up with your two most favourite fruits, either from the list or from your previous choices. Textboxes will appear when the time is up.";
function startTypingTimer() {
that.enableNextButton();
typingTimer_ = setInterval( function(){
if (typingTimeLimit > 0) {
document.getElementById("s5").innerHTML=pad(--typingTimeLimit%60);
document.getElementById("minutes5").innerHTML=pad(parseInt(typingTimeLimit/60,10));
}
if (typingTimeLimit == 0) {
clearInterval(typingTimer_);
jQuery("#NextButton").click();
}
}, 1000);
}
/*
$(function startThinkingTimer() {
that.disableNextButton();
thinkingTimer_ = setInterval( function(){
if (thinkingTimeLimit >0) {
document.getElementById("s5").innerHTML=pad(--thinkingTimeLimit%60);
document.getElementById("minutes5").innerHTML=pad(parseInt(thinkingTimeLimit/60,10));
}
if (thinkingTimeLimit == 0) {
clearInterval(thinkingTimer_);
document.getElementById("s5").innerHTML="10";
document.getElementById("minutes5").innerHTML="00";
jQuery(".InputText").show();
document.getElementById("instructions5").innerHTML = "You now have 10 seconds to type in the two fruits. The page will automatically move on to the next page once time is up.";
startTypingTimer();
}
}, 1000);
});
*/
function startThinkingTimer() {
that.disableNextButton();
thinkingTimer_ = setInterval( function(){
if (thinkingTimeLimit >0) {
document.getElementById("s5").innerHTML=pad(--thinkingTimeLimit%60);
document.getElementById("minutes5").innerHTML=pad(parseInt(thinkingTimeLimit/60,10));
}
if (thinkingTimeLimit == 0) {
clearInterval(thinkingTimer_);
document.getElementById("s5").innerHTML="10";
document.getElementById("minutes5").innerHTML="00";
jQuery(".InputText").show();
document.getElementById("instructions5").innerHTML = "You now have 10 seconds to type in the two fruits. The page will automatically move on to the next page once time is up.";
startTypingTimer();
}
}, 1000);
}
function pad (val) {
return val > 9 ? val : "0" + val;
}
startThinkingTimer();
});
<div id="instructions5"> </div>
<div id="timeShower1">time: <span id="minutes5">00</span>:<span id="s5">15</span></div>
window.thinkingTimer_;
window.typingTimer_;
Qualtrics.SurveyEngine.addOnload(function(){
that = this;
var thinkingTimeLimit = 15;
var typingTimeLimit = 10;
jQuery(".InputText").hide();
$('NextButton').hide();
document.getElementById("instructions5").innerHTML = "You have 15 seconds to think about the prompt and come up with your two most favourite fruits, either from the list or from your previous choices. Textboxes will appear when the time is up.";
function startTypingTimer() {
that.enableNextButton();
typingTimer_ = setInterval( function(){
if (typingTimeLimit > 0) {
document.getElementById("s5").innerHTML=pad(--typingTimeLimit%60);
document.getElementById("minutes5").innerHTML=pad(parseInt(typingTimeLimit/60,10));
}
if (typingTimeLimit == 0) {
clearInterval(typingTimer_);
jQuery("#NextButton").click();
}
}, 1000);
}
$(function () {
that.disableNextButton();
thinkingTimer_ = setInterval( function(){
if (thinkingTimeLimit >0) {
document.getElementById("s5").innerHTML=pad(--thinkingTimeLimit%60);
document.getElementById("minutes5").innerHTML=pad(parseInt(thinkingTimeLimit/60,10));
}
if (thinkingTimeLimit == 0) {
clearInterval(thinkingTimer_);
document.getElementById("s5").innerHTML="10";
document.getElementById("minutes5").innerHTML="00";
jQuery(".InputText").show();
document.getElementById("instructions5").innerHTML = "You now have 10 seconds to type in the two fruits. The page will automatically move on to the next page once time is up.";
startTypingTimer();
}
}, 1000);
});
/*
function startThinkingTimer() {
that.disableNextButton();
thinkingTimer_ = setInterval( function(){
if (thinkingTimeLimit >0) {
document.getElementById("s5").innerHTML=pad(--thinkingTimeLimit%60);
document.getElementById("minutes5").innerHTML=pad(parseInt(thinkingTimeLimit/60,10));
}
if (thinkingTimeLimit == 0) {
clearInterval(thinkingTimer_);
document.getElementById("s5").innerHTML="10";
document.getElementById("minutes5").innerHTML="00";
jQuery(".InputText").show();
document.getElementById("instructions5").innerHTML = "You now have 10 seconds to type in the two fruits. The page will automatically move on to the next page once time is up.";
startTypingTimer();
}
}, 1000);
}*/
function pad (val) {
return val > 9 ? val : "0" + val;
}
//startThinkingTimer();
});

hide and show div every x time AngularJS

I'm having troubles to hide and show a div that works as an alert to my application.
Currently I'm using the $interval for make this a permanent hide and show action, but the result I'm expecting is that the DIV remains visible X time and then hide the same X time.
Here is how I0'm doing it now:
function showNotification(idNotification) {
$('[id*=noti_]').addClass('dis_none');
$('#noti_' + idNotification).removeClass('dis_none');
}
function hideNotification() {
// $('#noti_' + idNotification).addClass('dis_none');
$('[id*=noti_]').addClass('dis_none');
}
function checkCalendar() {
var tomorrow = moment().add(1, "d").format("YYYY-MM-DD");
WebApiFactory.GetShiftPeriod("BodyShop", "2017-11-07").then(function (data) {
// WebApiFactory.GetShiftPeriod("BodyShop", tomorrow).then(function (data) {
if(data[0].TargetPlantValue === 0){
showNotification("alert");
}
});
}
function notifications(type, time) {
switch (type) {
case "calendar":
// checkCalendar();
$interval(function () {
checkCalendar();
console.log("Active");
},time * 1000);
$interval(function () {
hideNotification();
console.log("Hide");
}, time * 1001);
break;
}
}
Thanks for the help.
Not sure what are you trying to achieve, but if you want to show the dialog for some 'x' time, and then hide it, you shouldn't start both intervals at the same time. Just wait when the dialog is shown and then start a timer for hiding it.
For example if you need to hide the timer after a '100' ms.
function notifications(type, time) {
switch (type) {
case "calendar":
$interval(function () {
checkCalendar();
$timeout(hideNotification, 100);
}, time * 1000);
break;
}
}
Also be aware that I used a $timeout directive here. It's almost the same as $interval but will be invoked only once.
how can I make that the time that the div is shown is the same as the
time when is hide
It's a bit trickier, so let's use another algorithm.
There we just have a single $interval, but keep a current state isNotificationActive and show/hide the element according to this state.
Also be aware that I use $interval.cancel to stop a previous launched interval, if you have one.
var notificationInterval = null,
isNotificationActive = false;
function notifications(type, time) {
switch (type) {
case "calendar":
$interval.cancel(notificationInterval);
notificationInterval = $interval(updateNotificationState, time * 1000);
break;
}
}
function updateNotificationState() {
if(isNotificationActive) {
//hide the element here;
} else {
//show the element here;
}
isNotificationActive = !isNotificationActive;
}
I would do something like this ...
Make your notification element(s) "responsible" for hiding themselves, as follows :
function showNotification(idNotification, hideAfter) {
var $el = $('#noti_' + idNotification);
$timeout.cancel($el.data('timoutRef')); // kill latent auto-hide (if scheduled)
$el.removeClass('dis_none'); // show
if(hideAfter) {
// Schedule auto-hide after `hideAfter` milliseconds,
// and keep a reference to the timeout so it can be cleared.
$el.data('timoutRef', $timeout(function() {
$el.addClass('dis_none'); // hide
}), hideAfter);
}
}
Now adjust checkCalendar() and notifications()
function checkCalendar() {
WebApiFactory.GetShiftPeriod("BodyShop", "2017-11-07").then(function (data) {
if(data[0].TargetPlantValue === 0) {
// Make sure the value passed below is one half the total cycle time
showNotification("alert", 1000/2); // show immediately, hide after 1/2 second
}
});
}
function notifications(type, time) {
switch (type) {
case "calendar":
// Here, everything can be nice and simple
$interval(checkCalendar, time * 1000); // total cycle time
break;
}
}
Providing your various notification elements don't try to occupy the same real-estate on screen, you (probably) don't need to worry about hiding other notifications.
If the notification elements do try to occupy the same real-estate, you need to consider reducing their number to just one.

Only run a JavaScript function if it is not run again after a set amount of time

I have an input which controls the state of an element changing very rapidly. This causes that element to flicker as parts of it change.
I am trying to store these state changes and then providing nothing has changed for a set amount of time (an arbitrary 500ms) change the state.
I have tried to solve this using timeouts as demonstrated in the code below (the same code as in the fiddle.):
var changingToHappy = false;
// Original no attempts to fix functions.
//var ifHappy = function () {
// $("#face").text(':)');
//};
//
//var ifNotHappy = function () {
// $("#face").text(':(');
//};
var ifHappy = function () {
changingToHappy = true;
setTimeout(function () {
if (changingToHappy) {
$("#face").text(':)');
}
}, 500);
};
var ifNotHappy = function () {
changingToHappy = false;
setTimeout(function () {
if (!changingToHappy) {
$("#face").text(':(');
}
}, 500);
};
$("#textBox").keypress(
function (event) {
if (event.which == 49) {
ifHappy();
$("#flickerFace").text(':)');
}
if (event.which == 50) {
ifNotHappy();
$("#flickerFace").text(':(');
}
}
);
If you rapidly press 1, 2, 1, 2 and so on in the fiddle the face will remain not flickery for a moment and then the timeouts will catchup and it will begin to change state.
This fiddle http://jsfiddle.net/9w70wxgz/4/ simulates the problem.
To clarify I only want the face to change if nothing has tried to change its state for a set amount of time.
What you're looking for is called a debounced function, here is an example with a piece of your code (you're almost there):
//storage for timer
var notHappyTimer;
var ifNotHappy = function () {
changingToHappy = false;
//removes timer if event fires in less than 500ms
clearTimeout(notHappyTimer);
//resets it to attempt again in 500ms
notHappyTimer = setTimeout(function () {
if (!changingToHappy) {
$("#face").text(':(');
}
}, 500);
};
As you can see, you just assign the timeout to a variable that clears itself every time the function is fired, then starts the timer again. This ensures that the text change only happens if the function hasn't been fired in 500ms.

Increase value by 1 till mouse is release using angular mouse events

I have a td span which displays some numeric value and i have 2 icons (up/down arrow) shown above and below the span. Clicking on the up arrow increases the value by 1 till 99 and clicking on down arrow decreases the value by 1 till 0. Everything is working fine with ng-click.
The problem is if i want to increase the value say from 10 to 20, i have to click the up arrow 10 times. I want to do this by keep pressing the mouse on the up arrow. It should keep increasing/decreasing till i release the mouse on the up/down arrow respectively.
I tried using ng-mousedown but it increases by 1. Is there a way i can call the js function till the mouse button is released.
Any pointers will be helpful.
Code I tried so far:
var mouseDown = true;
var interval;
$scope.keepPressing = function(){
mouseDown = true;
if (mouseDown){
doSome();
}
};
function doSome(){
console.log(mouseDown);
if (mouseDown){
console.log(1);
interval = setInterval(doSome, 1000);
}
}
$scope.isMouseDown = function (){
clearInterval(interval);
mouseDown = false;
};
HTML:
The problem with this is even though the value 1 is not printed when i release the mouse, value false (mousedown value) is getting logged infinitely. This is a just the test flow to check if it works for me before i apply it to my actual function.
The output from console:
true
1
true
1
true
1
true
1
true
1
true
1
true
1
true
1
63 false
After i release the mouse, the false value is printed 63 times within seconds. I have to refresh the page to stop it.
Here is directive, that enables setting your own time-interval and step:
.directive('changingValue', function(){
return {
scope : {
changingValue :"=",
changingValueStep : "#",
changingValueInterval : "#"
},
link : function(scope, element, attrs) {
var step = attrs.changingValueStep;
if(!step)step = 1;
else step = parseInt(step);
var interval = attrs.changingValueInterval;
if(!interval)interval = 500;
else interval = parseInt(interval);
console.log("step", step, "interval", interval);
var pressed = false;
function changeValue() {
if (pressed) {
setTimeout(
function() {
console.log(scope.changingValue);
scope.changingValue += step;
scope.$apply();
changeValue();
}, interval
)
}
}
element.on("mousedown", function() {
pressed = true;
changeValue();
});
element.on("mouseup", function() {
pressed = false;
});
}
}
Basically it can be used like that:
<div changing-value="counter" changing-value-step="-1" changing-value-interval="200">-</div>
And here is working --->PLNKR<---.
Here is what i did and working for me.
var interval;
scope.mousePress = function(){
doSome();
}
};
var doSome = function {
increaseValue(); // separate function where all the increment logic resides.
interval = $timeout(doSome, 1000);
}
scope.mouseRelease = function (){
$timeout.cancel(interval);
};
In the HTML, call the mousePress & mouseRelease functions from ng-mousedown & ng-mouseup respectively.

How can i do button on hold for two second call function1 if less then two second call function2?

I have this button which is not working correctly for hold button for a period (but it works like click only).
Where i was trying to do if the button is hold for greater/equal then 2 seconds then callfunction1, if the button was pressed less then 2 seconds then callfuntion2.
var clickDisabled = false;
function clickLocker() {
/* #Button: 2 seconds */
clickDisabled = true;
setTimeout(function(){clickDisabled = false;}, 2000);
}
function callfunction1() { // you have hold he button for greater then or equal 2 second }
function callfunction2() { // you have hold the button less then 2 second }
$('.button').live("click",function()
{
if (clickDisabled) {
alert("locked for 2 second");
return;
}
clickLocker();
});
I think this solution should work. I have not tested it but it should give you the right idea.
var startTime;
function callfunction1() { // you have hold he button for greater then or equal 2 second }
function callfunction2() { // you have hold the button less then 2 second }
function buttonDownEvent() {
var Time = new Date();
startTime = Time.getTime();
}
function buttonUpEvent() {
if(new Date().getTime() - startTime < 2000)
callfunction2()
else
callfunction1()
}
$('.button').live("mousedown",function()
{
buttonDownEvent();
});
$('.button').live("mouseup",function()
{
buttonUpEvent();
});
Listen for both events, mousedown and mouseup, measuring the time between both:
var timeDown;
var timeUp;
$('.button').live("mousedown",function(){
timeDown = event.timeStamp;
});
$('.button').live("mouseup",function(){
timeUp = event.timeStamp;
time = timeUp-timeDown;
if (time>2000){
function1();
}else{
function2();
}
});
please note that event.timeStamp wont work well in firefox. For firefox you can do (new Date).getTime();
You can do this using events to the mouseup and mousedown events and timing the difference between them. Also, you need to remember which element caused the click - if the user released the mouse on a different element then it should just do the "non 2-second hold" function. A JSFiddle showing this working is available here: http://jsfiddle.net/35rw3/6/.
That was a great suggestion from slash. This is how you can do this
var clickstart;
var clickstop;
$("a").on('mousedown', function(e) {
clickstart = e.timeStamp;
}).on('mouseup', function(e) {
clickstop = e.timeStamp- clickstart
if(clickstop >= 2000) two()
else one();
});
Demo
Updates:
It might be necessary to track the mouse movement like #MarkRhodes wrote in his comments. So for that, check this update

Categories