Angularjs and Jquery: Angularjs routing and jquery script execution issue - javascript

i have tried using angularjs and jquery for developing my websites and i'm not new to them. but somehow i stumbled to an issue which is not so familiar for me.
the main problem is between angular routing and jquery document ready script execution.
the scenario is i use angularjs ui-router to browse pages for my website(SPA) one of my page has inline jquery codes looks like this
<div id="details-page">
</div>
<script>
var counter = 0;
function foo() {
var setFoo = setInterval(function() {
counter++;
console.log(counter);
}, 2000);
}
</script>
foo();
the codes runs as expected,
but when i go route to another pages i can see my logs still running(the foo function) which seems weird for me but that's not the main problem, the main problem is when i go back to "details-page" the function foo is executed back(because of $(document).ready(function(){})) so two foo's is running in my system which is definitely destroyed everything.
so my goal here is how to STOP the old foo function or
how to STOP the new function foo from executing in short i just want one foo function running in my system, thanks

If I understand you correctly you want this counter to start when the user enters the "details-page", you want it to keep running when they leave, and when they come back it must use the original counter.
To solve this with AngularJS you should create a service, as your service will be a singleton and not linked specifically to the "details-page".
Fiddle example: https://jsfiddle.net/UncleDave/g9co2172/1/
angular
.module('app', [])
.controller('DetailsCtrl', function(counterService) {
counterService.start();
})
.service('counterService', function($interval) {
var counter = 0;
var started = false;
function start() {
if (started)
return;
started = true;
$interval(function() {
counter++;
console.log(counter);
}, 2000);
}
return {
start: start
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="DetailsCtrl as details">
</div>

just add this code in your controller,and don't forget t inject $rootScope and $interval services in your controller.
$rootScope.counter = 0;
var setFoo;
function foo() {
setFoo = $interval(function () {
$rootScope.counter++;
console.log($rootScope.counter);
}, 2000);
}
foo();
$rootScope.$on("$routeChangeStart", function () {
$interval.cancel(setFoo);
})

Related

Angular: how to keep resetting $timeout on scroll events, and let $timeout finish otherwise

I'm working on a little controller that watches for a scroll event and applies a CSS class, and applies a different CSS class. Long story short I'm trying to have the scrollbar thumb disappear when you're not scrolling, and appear when you ARE scrolling (like the scrollbar thumb on an iPhone).
I'm having trouble implementing it. My thought process doing this is:
1) On page load, set a $scope variable to false.
2) Watch for a scroll event on the div I want.
3) Once the scroll event starts, set the $scope variable to true.
4) Keep on resetting the $timeout whenever a scroll event fires.
5) In the timeout function, set the $scope variable back to false if the $timeout finishes.
6) In the HTML, set an ng-class to watch for this $scope variable.
I thought that sounded pretty simple, but I'm having a lot of trouble implementing it and I'm not sure if it's just something about $timeout that I'm missing, or if I'm just thinking in circles and don't realize it yet.
This is the controller I have set up for it (An actual working JSFiddle is linked beneath this wall of code):
(function () {
'use strict';
angular
.module('scrollingApp')
.controller('scrollbarController', scrollbarController);
function scrollbarController($scope, $timeout) {
$scope.actuallyScrolling = false;
$scope.scrolling = function() {
$('.container').on('scroll', function(event) {
$scope.actuallyScrolling = true;
console.log('before checkScroll ', $scope.actuallyScrolling);
checkScroll();
});
};
var checkScroll = function() {
var timeoutEnded = false;
$timeout(function() {
if($scope.actuallyScrolling) {
$scope.actuallyScrolling = false;
console.log('inside $timeout if statement', $scope.actuallyScrolling);
}
}, 1000);
console.log($scope.actuallyScrolling);
};
$scope.scrolling();
}
})();
I set up a JSFiddle here (https://jsfiddle.net/hurgledurf/k5naeora/) with the code I have so far (hopefully it's self-explanatory), and would appreciate any help/insight anyone might have. Thank you!
Angular of not... To "reset" a timeout on scroll should be done this way:
var timer;
$('.container').on('scroll', function(event) {
console.log('User is actually scrolling...');
clearTimeout(timer);
timer = setTimeout(function(){
console.log('User finished scrolling.');
},500);
});
It replace this chunk of your code:
$('.container').on('scroll', function(event) {
$scope.actuallyScrolling = true;
console.log('before checkScroll ', $scope.actuallyScrolling);
checkScroll();
});

Why ng-class is not updating using setTimeout? Angular

I have a function 'highlightBookmark' that should change the background color of a list item after 2 seconds. But it doesn't work!!!
It changes 'li' background only if the function is called by click event. It doesn't change it automatically after time is out, even if it actually calls the function.
Here is my code:
Controller.js
//markers
$scope.markers = [
{
time: 9.5,
text: "Bookmark 1",
class: false
},
{
time: 106,
text: "Bookmark 2",
class: false
}
]
$scope.currentBookmark = -1;
function highlightBookmark(index) {
$scope.markers[index].class = true;
}
var interval = setInterval(checkTime, 100);
function checkTime(){
if(Math.floor(player.currentTime()) == 2){
highlightBookmark(1)
clearInterval(interval);
}
}
$scope.jumpTo = function (index) {
highlightBookmark(index);
}
The highlight function, takes in an integer, looks for object at that position and updates set 'class' parameter to true. Example, if I pass number 1 to the function, it will look for object at index 2 and set the 'class property' to be true.
Then, after 2 seconds I want to call the highlightBookmark function. IT IS CALLED but it doesn't update the class, thus the background doesn't update.
I call the same with click event and it works.
HTML file
<ul id = "bookmarkList">
<li ng-repeat="bookmark in markers" ng-class="{'active': bookmark.class}" ng-click="jumpTo($index)">{{bookmark.text}}</li>
</ul>
This li has the ng-class property that I want to change after 2 seconds.
Here is a link to a similar code I did on codepen. It changes button color on click, but doesn't change on setTimeout even if method is called
https://codepen.io/Octtavius/pen/wgzORv
Could somebody help with this simple issue?
The vanilla setInterval function doesn't update scope variables. Try with the $interval API by Angular:
var interval = $interval(checkTime, 100);
function checkTime(){
if(Math.floor(player.currentTime()) == 2){
highlightBookmark(1)
interval.cancel();
}
}
Notice clearInterval(interval) changes to interval.cancel()
Also don't forget to inject it as dependency.
Fair point by charlietfl: Also cancel the interval if the scope gets destroyed.
Place this inside your controller:
$scope.$on("$destroy", function( event ) {
interval.cancel( timer );
});
More info: https://docs.angularjs.org/api/ng/service/$interval
setInteral and setTimeout run outside of the angular digest cycle, so they will not be properly picked up by Angular.
Consider using the $timeout object in your controller -- this gives you the timeout functionality but allows angular to keep an eye on it.
You should consider use angular $timeout instead of setInverval or setTimeout.
Because:
This functions don't $digest the $scope variables;
$timeout in this case requires less memory to do the exactly same thing that $interval would do.
So, this part of your controller will look like this:
//markers
$scope.markers = [
{
time: 9.5,
text: "Bookmark 1",
class: false
},
{
time: 106,
text: "Bookmark 2",
class: false
}
]
$scope.currentBookmark = -1;
function highlightBookmark(index) {
$scope.markers[index].class = true;
}
$timeout(checkTime, 2000);
function checkTime(){
highlightBookmark(1);
}
$scope.jumpTo = function (index) {
highlightBookmark(index);
}
Remember to inject the $timeout as a dependency in your controller.
P.S.
This code will mark a default after 2 seconds, you don't give enough details so I can know what the player is doing. So if you want to improve this, give more details and we can make it happen.
You should consider use "controller as". Here is a link to John Papa's article about it. https://johnpapa.net/angularjss-controller-as-and-the-vm-variable/
And if you want to toggle the background on click event you should use this code. Cause the one you did is only adding the background, but not removing from the others li. To do this we need to modify the html and the controller a little bit:
<div ng-app="classApp" ng-controller="classCtrl">
<ul id = "bookmarkList">
<li ng-repeat="bookmark in markers" ng-class="{'active': selectedMarker === bookmark}" ng-click="jumpTo(bookmark)">{{bookmark.text}}</li>
</ul>
</div>
$scope.selectedMarker = null;
function highlightBookmark(marker) {
$scope.selectedMarker = marker;
}
$timeout(checkTime, 2000);
function checkTime(){
highlightBookmark($scope.markers[0])
}
$scope.jumpTo = function (marker) {
highlightBookmark(marker);
}
Cya.
var classApp = angular.module('classApp', []);
classApp.controller('classCtrl', function ($scope, $timeout) {
$scope.isActive = false;
$scope.activeButton = function () {
$scope.isActive = !$scope.isActive;
}
function checkTime() {
$scope.isActive = !$scope.isActive;
}
$timeout(checkTime, 2000)
});

Why is 'this' losing context in new Angular 1.5 components?

Got a very odd issue coming up here with the new components. When we had a 1.4 directive we had the following code...
(function () {
'use strict';
angular.module('app.board').directive('dcCb', dcClipboardCopy);
function dcCb() {
return {
link : function(scope, elem) {
var clipboard = new Clipboard(elem[0]);
elem.on('$destroy', function() {
clipboard.destroy();
});
}
};
}
})();
Inside the clipboard.destroy() function is the following...
Clipboard.prototype.destroy = function(){
this.listeners.destroy();
}
In 1.4 this is the same as the element so...
<button class="btn btn-sm btn-menu-outline copy-button" ...
So this worked fine as the button element seemed to have the listeners property which could be invoked.
However after the upgrade to 1.5 and now we have a component like this....
(function() {
'use strict';
angular.module('app.board').component('dcCb', {
...
controller: [ '$element','$scope',function($element,$scope) {
var self = this;
self.$postLink = postLink;
function postLink(){
var clipboard = new Clipboard($element[0]);
...
$element.on('$destroy', clipboard.destroy);
}
}]
});
})();
this (when inside the destroy function of the Clipboard) is now the controller object. So trying to call this.listeners throws an error.
First Question :
I understand that this in new components is the component scope but in 1.4 it was the button element. Surely in both the button element should be $element? Were we doing something wrong in 1.4?
Second Question :
Shouldn't var clipboard = new Clipboard($element[0]) force the context of this inside the clipboard to always be the clipboard itself (due to the new keyword)?
You're handing a function, which is arbitrarily defined on a class, off to the window and event listeners to be executed in a different context than the instance of Clipboard:
$element.on('$destroy', clipboard.destroy);
This is a fundamental concept of execution context in javascript, and I'd recommend reading up on it. But you can easily solve your current problem by simply binding the context of the function you are passing:
$element.on('$destroy', clipboard.destroy.bind(clipboard));

Scroll to bottom of page after get request AngularJs

I'm familiar with using something like:
$scope.gotoBottom = function(){
$location.hash('bottom');
$anchorScroll();
}
and this works.. yet what I'm seeing is an issue when retrieving data that's being used in an ng-repeat and attempting to resize when that data comes in.
Example (in controller):
users.get({userList:$routeParams.conversationId}, function(data){
$scope.userList = data;
$scope.gotoBottom();
})
The gotoBottom method is firing to fast, while the ng-repeat is looking on $scope.userList and buidling it's table based off that.
I want to be able to toggle gotoBottom after that list has been remade (or whenever it's modified). Is there a better way to achieve this?
Thank you!
You can use $watch listener to fire gotoBotton when an AngularJs variable change.
$scope.ActivityList = new Array();
$scope.$watch("ActivityList", function () {
$scope.$evalAsync(function () {
$scope.DoSomething();
});
}, true);
$scope.DoSomething = function () {
$(function () {
//$scope.gotoBottom();
});
};
Also you can run scrolling bottom after page is loaded
angular.element($window).bind('load',
function() {
var element = document.getElementById("messages-list").lastElementChild;
element.id = "bottom";
/*-------------*/
$location.hash('bottom');
$anchorScroll();
}

Change reload interval to waiting after the last reload

My application reloads data every 500ms. How do I have to change the code to not reload every 500ms but to wait for 500ms after the last reload to trigger a new one?
App = Ember.Application.create({
ready: function() {
var switchboard = App.Switchboard.find(switchboard_id);
setInterval(function() {
switchboard.reload();
}, 500);
}
});
I have just done something similar. You should use activate property on your route (http://emberjs.com/api/classes/Ember.Route.html#method_activate).
Checkout this pull request: https://github.com/chrmod/rowmark/pull/2/files
Some example:
App.NoteRoute = Ember.Route.extend({
activate: function() {
this.interval = setInterval(function() {
this.get('controller').set('toSave', true);
}.bind(this), 5000);
}
})
UPDATE
I understand you wrong. Sorry for that.
First of all you need to know that find from Ember Model or Ember Data returns promises (http://emberjs.com/blog/2013/05/28/ember-data-0-13.html)
I think you can do such trick to implement that:
App = Ember.Application.create({
ready: function() {
var switchboard;
setInterval(function() {
switchboard = App.Switchboard.find(switchboard_id).then(function(){
setTimeout(function(){}, 499);
});
}, 1);
}
});
First of all we run setInterval to run this in infinity loop. Next in each loop iteration we find Switchboard and when Ember data loads from external server those data that run function that is passed to then. This function simply wait 499ms :)

Categories