Reset scope interval in angularjs directive creating loop - javascript

In my agnularjs directive I've got object containting logic of some script
$scope.slider = {
increment: 0,
step: 1,
positionTop: 0,
init: function (step, isClick) {
if(isClick) clearInterval($scope.interval);
...rest of script...
and then below, after $scope.slider is closed (}) I have this code
$timeout(function () {
$scope.slider.changeSlide($scope.slider.step);
$scope.interval = setInterval(function () {
$scope.slider.step++;
if($scope.slider.step === 5) $scope.slider.step = 1;
$scope.slider.changeSlide($scope.slider.step);
}, 5000);
});
On page load I'm starting init method work and then when user additionaly click this html tag
<span class="main__last-added-dot"
data-ng-class="slider.step == 1 ? 'main__last-added-dot--active' : ''"
data-ng-click="slider.init(1, true);"></span>
it clear interval which means that it stop working and I'm not seting new interval because I dont know how. I'm using angularjs $timeout to start interval because div which I'm working on are gathered in backend call so I need to wait for them.
I tried to assign interval to another scope variable and call it within like this
in init method within $scope.slider object:
$timeout(function(){ $scope.startInterval() });
below $scope.slider object:
$timeout(function () {
$scope.startInterval = function() {
$scope.interval = setInterval(function () {
console.log('fire function interval');
$scope.slider.step++;
if($scope.slider.step === 5) $scope.slider.step = 1;
$scope.slider.changeSlide($scope.slider.step);
}, 5000);
};
$scope.startInterval();
});
But it create some kind of loop I don't really know it was working very strange.
What I'm doing wrong how to stop this interval and start it again, after span click i want to clear seconds to 0..
I add demo.

You can do something like this below code, also please check this working plunker example for your given scenario.
Directive:
app.directive('myElement', ['$interval', '$timeout',
function($interval, $timeout) {
return {
restrict: 'E',
templateUrl: 'template.html',
link: function(scope) {
scope.timer=5000;
scope.step = 1;
function updateTime() {
if(scope.step==4) scope.step=0;
scope.step++;
}
scope.stopTime = $interval(updateTime, scope.timer);
scope.resetTimer=function(step){
scope.step = step;
$interval.cancel(scope.stopTime);
scope.stopTime = $interval(updateTime, scope.timer);
}
}
}
}]);

You should name your function and reset its interval in init like this:
$scope.slider = {
increment: 0,
step: 1,
positionTop: 0,
init: function (step, isClick) {
if(isClick) clearInterval($scope.interval);
$scope.interval = setInterval(intervalSlide, 5000); // Reset interval here
...rest of script...
};
// Name your function, no need to attach it to $scope
function intervalSlide() {
$scope.slider.step++;
if($scope.slider.step === 5) $scope.slider.step = 1;
$scope.slider.changeSlide($scope.slider.step);
}
$timeout(function () {
$scope.slider.changeSlide($scope.slider.step);
$scope.interval = setInterval(intervalSlide, 5000);
});

Related

How to check if all documents are loaded with Firebase.util pagination

How can I check if I have to stop calling the loadMore() function, because all the documents have already been loaded from the database?
In the example below I'm using Ionic, but it's the same also with ng-infinite-scroll in AngularJS apps.
This is my actual code:
HTML:
...
<ion-infinite-scroll
ng-if="!noMoreItemsToLoad"
on-infinite="loadMore()"
distance="5%">
</ion-infinite-scroll>
</ion-content>
JS Controller:
$scope.loadMore = function(){
console.log('Loading more docs...');
Items.loadMore(); // calling the .next() method inside the Items service
if( !Items.hasNext()) { $scope.noMoreItemsToLoad = true; }
$scope.$broadcast('scroll.infiniteScrollComplete');
}
JS Items Factory:
.factory('Items', function (FIREBASE_URL, $firebaseArray, $firebaseObject) {
var itemsRef = new Firebase(FIREBASE_URL + 'items/');
var scrollRef = new Firebase.util.Scroll(itemsRef, 'name');
var self = {
getAllItems : function(){ ... },
loadMore: function(){
scrollRef.scroll.next(4);
},
hasNext: function(){
if(scrollRef.scroll.hasNext()) { return true; }
else { return false; }
}
}
return self;
}
Do the scroll.next in timeout, for example:
loadMore: function(){
$timeout(function() {
scrollRef.scroll.next(4);
});
},
I had the same issue and I think the solution is to modify the hasNext() function on firebase.util.js:
Cache.prototype.hasNext = function() {
return this.count === -1 || this.endCount >= this.start + this.count;
};
I put a missing equal sign (=) before this.start
I hope it works for you.

AngularJS scope doesn't update until do something in ui, like click a object with a function

I am trying to add class to my sidebar when the viewport is less than or equal to 641px, and I have to watch the width of the window for that case
$scope.$watch(function(){
return $window.innerWidth;
}, function(value) {
if (value <= 641) {
logger.info('!!!less than 641');
vm.singleColumn = true;
};
});
It logs on first load, but when I resize, I have to do some clicks before it triggers again. E.g. I resized then I clicked on an item with ng-click behavior then that is the only time, it logs again.
I've read some of the questions, and it is likely due to $digest and $apply?
Can someone give a light to my confusion.
You need to trigger the digest cycle, or the view does not realize that the value was updated.
$scope.$watch(function(){
return $window.innerWidth;
}, function(value) {
if (value <= 641) {
logger.info('!!!less than 641');
vm.singleColumn = true;
$scope.$apply();
};
});
According to me you can try directive and check the window resize using jquery and update your variable accordingly. I got an example here please check
var app = angular.module('miniapp', []);
function AppController($scope) {
/* Logic goes here */
}
app.directive('resize', function ($window) {
return function (scope, element) {
var w = angular.element($window);
scope.getWindowDimensions = function () {
return {
'h': w.height(),
'w': w.width()
};
};
scope.$watch(scope.getWindowDimensions, function (newValue, oldValue) {
scope.windowHeight = newValue.h;
scope.windowWidth = newValue.w;
scope.style = function () {
return {
'height': (newValue.h - 100) + 'px',
'width': (newValue.w - 100) + 'px'
};
};
}, true);
w.bind('resize', function () {
scope.$apply();
});
}
})
div {
border:1px solid red;
}
<div ng-app="miniapp" ng-controller="AppController" ng-style="style()" resize>window.height: {{windowHeight}}
<br />window.width: {{windowWidth}}
<br />
</div>
fiddle
please let me know if this works
1) What user1162084 says
2) The approach with watch on function(){return $window.innerWidth;} will not work, because the resize of window do not cause the start of $digest cycle.
The $watch expression is reevaluated only in $digest cycle. No $digest cycle = no reevaluation. In angularjs $digest cycle is started:
a) After code in ng-click was executed
b) After function in $timeout or $interval was executed.
c) After http request made with $http was finished and success\error handler was executed
There may be and other cases, but the point is that resize of window do not belong to those types of events, which lead to start of $digest cycle.
And this also explains why with code you provided you get update only after ng-click execution

AngularJS + Ionic is truncating scope value after the return from second page to the main page.

I have following code in the "First" Ctrl of the my app, which is displaying timer for countdown.
Everything is working absolutely fine until i visited second page which is in the app.js defined by this way:
.state('app.home', {
url: '/home:playlistData',
views: {
'menuContent' :{
templateUrl: 'templates/home.html',
controller: 'HomeCtrl'
}
}
})
.state('app.saved', {
url: '/saved',
views: {
'menuContent' :{
templateUrl: 'templates/saved.html',
controller: 'SavedCtrl'
}
}
})
If i came back from second view to the first counter is still displayed but value in the
$scope.minutesLeft
Is not updated but
setInterval
function is still executing the code in the background and updated data values are still holded in the Dataholdingservice.
I tried scope apply and timeout functions, but without the luck.
Could somebody tell me how can i solve this issue?
Many thanks for any help.
Code of the HomeCtrl for countdown timer is following:
$scope.setTimer = function(timer) {
console.log(timer);
$scope.timer = timer;
};
$scope.saveTimer = function(timer) {
if($scope.selectedSounds.length == 0) {
$scope.showAlert("Add some sounds", "Cannot run timer for empty list");
} else {
$scope.clearCountDownAnimation();
var animationTimerId = setInterval(function () {
$("#minutesLeft").fadeTo(100, 0.1).fadeTo(200, 1.0);
}, 1000);
Dataholdingservice.setAnimationId(animationTimerId);
Dataholdingservice.setMinutesLeft(timer);
$scope.closePopover();
$scope.countDown();
}
};
$scope.clearCountDownAnimation = function() {
$("#minutesLeft").clearQueue().finish();
// Clear previously set animations
console.log(Dataholdingservice.getAnimationId());
if (Dataholdingservice.getAnimationId() != null) {
console.log("Interval cleared");
clearInterval(Dataholdingservice.getAnimationId());
}
};
$scope.countDown = function() {
var minutesLeft = Dataholdingservice.getMinutesLeft();
$scope.minutesLeft = minutesLeft;
$scope.isCounterDisplayed = Dataholdingservice.isCounterDisplayed();
var timerId = setInterval(function() {
console.log("Counting down");
minutesLeft -- ;
console.log("Decreasing minutes");
console.log(minutesLeft);
Dataholdingservice.setMinutesLeft(minutesLeft);
console.log("minutes left " + Dataholdingservice.getMinutesLeft());
$scope.$apply(function () {
$scope.minutesLeft = Dataholdingservice.getMinutesLeft();
});
if(minutesLeft <= 0) {
console.log("Time left");
clearInterval(Dataholdingservice.getTimerId());
clearInterval(Dataholdingservice.getAnimationId());
console.log(Dataholdingservice.isCounterDisplayed());
$scope.hideCounter();
$scope.stopAllSelectedSounds();
}
}, 1000 * 1);
Dataholdingservice.setTimerId(timerId);
};
$scope.hideCounter = function() {
console.log("Hidding the counter");
$scope.isCounterDisplayed = false;
$scope.$apply();
};
$scope.cancelTimer = function() {
clearInterval(Dataholdingservice.getTimerId());
clearInterval(Dataholdingservice.getAnimationId());
$("#minutesLeft").hide();
$ionicLoading.show({
duration: 500,
template: 'Timer canceled'
});
};
Since the $scope.minutesLeft is a primitive datatype, sometimes the changes happening in the controller will not get reflected in the view. You can create an object like $scope.viewModel = {} and then add the minutesLeft as a property to it like $scope.viewModel.minutesLeft = mintesLeft in your countdown function and bind viewModel.minutesLeft to the view. you should see the value getting updated properly.
I am not sure of your exact requirement, but I have put together the code for creating a simple timer that runs in the background in an angular service. The working code is available at http://codepen.io/svswaminathan/pen/MYXOPM

reset timer on click using javascript

i have this JS Code:
<script>
$(document).ready(function () {
window.setTimeout(function () {
$('#logout_warning').reveal();
}, 6000)
});
$(document).ready(function () {
window.setTimeout(function () {
location.href = "/login/logout.php?url=/index.php?r=inactivity";
}, 12000)
});
</script>
it displays a DIV after X Seconds then redirects the page after Y Seconds
is there a way to create a link that will reset the timer back to X seconds and without having to refresh the page?
You could write a wrapper for setTimeout like this
function myTimeout(fn, delay) {
var o = {i: null};
o.cancel = function () {
if (o.i) window.clearTimeout(o.i);
};
o.reset = function () {
if (o.i) window.clearTimeout(o.i);
o.i = window.setTimeout(fn, delay);
};
o.reset();
return o;
}
Then
// common (ancestor?) scope
var t;
// descendant scope, set
t = myTimeout(function () {
$('#logout_warning').reveal();
}, 6000);
// descendant scope where you attach listener to your link
$('#my_link').on('click', function () {
t.reset(); // re-start the timeout
return false;
});

Stop time interval during navigation in angularjs

Currently i am using angular js
I have one get method to get data from server side in default.html page
function bindActiveLoans() {
$http.get(rootUrlSingleEscaped + '/cooperativa/allunfundedloans/?authId=' + userInfo.UserId)
.success(function (response) {
if (response.Loans.length == 0) {
$scope.IsValues = false;
$scope.$apply();
return true;
}
$scope.IsValues = true;
$scope.unfundedLoans = response;
});
};
setInterval(function () {
$scope.$apply(bindActiveLoans());
}, 5000);
The above function helps to get the data from server side method in every 5 seconds.
Here, I have an issue.
Additionally I have some more pages, like
default2.html,default3.html,default4.html,default5.html.
The timer is still running while i navigation default.html page to default1.html page. I want to only that timer running in default.html page.If i go to another page,then the timer should stop. How can we achieve it?
From the angularjs example in the $interval API page.
var interval = $interval(function() {
console.log('say hello');
}, 1000);
$interval.cancel(interval);
With $interval.cancel(var) you can stop the interval and if you want to stop it when navigating to another page you should try something like this.
var interval = null;
module.controller('SomeController', '$interval', function($interval) {
interval = $interval(function() {
...
}, 2000);
}).run(['$rootScope', '$interval', function($rootScope, $interval) {
$rootScope.$on('$routeChangeStart', function() {
$interval.cancel(interval);
});
}]);
You can listen to the $routeChangeStart event and do the clearing of the interval depending on the route parameters,
$scope.$on('$routeChangeStart', function (scope, next, current) {
if(<next is not default.aspx>){
// clear interval here
}
});
call clearInterval function when navigating from default page
e.g.
var myVar = setInterval(function () {
$scope.$apply(bindActiveLoans());
}, 5000);
function myStopFunction()
{
clearInterval(myVar);
}
Call myStopFunction

Categories