I have created a stopwatch factory service, doesn't really do much besides running (reset and other features not implemented, just ignore them).
I set a $scope.time to receive the changes of the timer, however it isn't being updated.
I have tried many different approaches the last one I guess is really bad practice but I wanted to try and make it work.
my view:
<ion-view view-title="Clock">
<ion-content>
<div ng-controller="controller as ClockCtrl">
<h1 ng-bind="ClockCtrl.time"></h1>
</div>
</ion-content>
</ion-view>
my Controller:
.controller('ClockCtrl', function($scope, Stopwatch) {
var vm = $scope;
$scope.time = "";
Stopwatch.init($scope);
})
my service:
factory('Stopwatch', function() {
var Stopwatch = (function() {
var s;
var isRunning = 0;
var time = 0;
var thatElement;
function run(element){
if(isRunning !== 0){
time++;
s.mills = time % 10;
s.secs = Math.floor(time / 10);
s.mins = Math.floor(time / 10 / 60);
//Stopwatch.currentTime = ("0"+s.mins).slice(-2)+":"+("0"+s.secs).slice(-2)+":"+("0"+s.mills).slice(-2);
thatElement.time = ("0"+s.mins).slice(-2)+":"+("0"+s.secs).slice(-2)+":"+("0"+s.mills).slice(-2);
setTimeout(run, 100);
}
};
return {
settings: {
currTime: "",
mills: 0,
secs: 0,
mins: 0,
i: 1,
times: ["00:00:00"],
},
currentTime: "",
init: function(element) {
thatElement = element;
s = this.settings;
this.start(element);
},
clear: function() {
s.i = 1,
s.mins = s.secs = s.mills = 0;
s.times = ["00:00:00"],
s.results.innerHTML = s.clearButton;
},
start:function(element){
isRunning = 1;
run(element);
},
stop: function(){
isRunning = 0;
}
}
})();
return Stopwatch;
})
I know that the last approach is bad, I would accept any pointers, this is my first time using ionic, 2nd time trying angular.
Thanks in advance!
Since you are using controllerAs syntax you need to set controller instance property, not $scope object one:
.controller('ClockCtrl', function($scope, Stopwatch) {
this.time = "";
Stopwatch.init(this);
});
Related
I have this angularJS app which has a countdown timer. I want to execute a function after it becomes zero. It currently goes into negative after it reaches 0. How to achieve this?
Angular Code:
myApp.controller('MyController', ['$scope', '$timeout' ,function($scope, $http, $routeParams, $timeout) {
$scope.counter = 5000;
$scope.onTimeout = function(){
$scope.counter--;
mytimeout = $timeout($scope.onTimeout,1000);
}
var mytimeout = $timeout($scope.onTimeout,1000);
}
}]);
testControllers.filter('formatTimer', function() {
return function(input)
{
function z(n) {return (n<10? '0' : '') + n;}
var seconds = input % 60;
var minutes = Math.floor(input / 60);
var hours = Math.floor(minutes / 60);
return (z(hours) +':'+z(minutes)+':'+z(seconds));
};
});
HTML:
<div><p>{{counter|formatTimer}}</p></div>
Simply stop calling it when the counter is 0:
$scope.onTimeout = function(){
if (--$scope.counter > 0) {
$timeout($scope.onTimeout, 1000);
} else {
// Call your function now that counter is 0
}
}
Depending on what else you are doing, you probably don't need to put your timeout method on the scope. Also, look at $interval instead. It's better suited for calling something continuously.
function decreaseCounter() {
if(--$scope.counter <= 0) {
$interval.cancel(intervalPromise);
yourOtherFunction();
}
}
var intervalPromise = $interval(decreaseCounter, 1000);
https://docs.angularjs.org/api/ng/service/$interval
If you need a precise countdown (specially if the time span is large) you need to calculate the time spent using Date time function. Simply incrementing a counter each 1000 milliseconds 5000 times, doesen't guarantee that at the end, the real time spent will be exactly 5000 seconds!
You could try something like this:
Controller:
function MyCtrl($scope, $interval) {
var intervalId;
$scope.counter = 0;
$scope.initialCountdown = 10;
$scope.countdown = $scope.initialCountdown;
$scope.timer = function(){
var startTime = new Date();
intervalId = $interval(function(){
var actualTime = new Date();
$scope.counter = Math.floor((actualTime - startTime) / 1000);
$scope.countdown = $scope.initialCountdown - $scope.counter;
}, 1000);
};
$scope.$watch('countdown', function(countdown){
if (countdown === 0){
$scope.stop();
}
});
$scope.start = function(){
$scope.timer();
};
$scope.stop = function(){
$interval.cancel(intervalId);
};
}
View:
<div ng-controller="MyCtrl">
<div>Counter: {{counter}}</div>
<div>Countdown in seconds: {{countdown}}</div>
<div>Countdown date time: {{countdown | secondsToDateTime | date:'HH:mm:ss'}}</div>
<button ng-click="start()">start</button>
<button ng-click="stop()">stop</button>
</div>
Filter secondsToDateTime:
myApp.filter('secondsToDateTime', [function() {
return function(seconds) {
return new Date(1970, 0, 1).setSeconds(seconds);
};
}])
CHECK THE DEMO FIDDLE
Enjoy!
I'm looking on how to make a 60 seconds countdown using angular js.
I want to show the countdown on the page ! and when the countdown is finished, the controller should reload to execute the code again ! and get the update json object !
my controller looks like :
.controller('todaymatches', function($rootScope,$scope, $http) {
$http.get("http://www.domaine.com/updatedjson/")
.success(function (response) {
$scope.matches = response;
});
})
I'm made a code ! I'm not sure if this works properly ! anyway it's not working on my app.
$scope.countdown = function() {
stopped = $timeout(function() {
console.log($scope.counter);
$scope.counter--;
$scope.countdown();
}, 1000);
};
Here is a simple countdown example:
HTML
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.0rc11.min.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
</head>
<body>
<div ng-controller="CountdownController">
{{counter}}
</div>
</body>
</html>
Javascript
function CountdownController($scope,$timeout) {
$scope.counter = 60;
$scope.onTimeout = function(){
if ($scope.counter > 0) {
$scope.counter--;
mytimeout = $timeout($scope.onTimeout,1000);
} else {
$scope.counter = 60;
}
}
var mytimeout = $timeout($scope.onTimeout,1000);
}
Demo
'use strict';
var ngApp = angular.module('myApp', ['Test']);
var c1 = angular.module('Test', []);
c1.controller('Ctrl1', function ($scope, $timeout) {
$scope.coutDown = function () {
$scope.onTimeout = function () {
console.log("value", $scope.value);
$scope.value = $scope.value - 1;
return $scope.coutDown($scope.value);
};
var delay = $timeout($scope.onTimeout, 1000);
if ($scope.value < 1) {
$timeout.cancel(delay);
return true;
}
return false;
};
$scope.value = 5;
$scope.coutDown();
});
<div ng-app="myApp">
<div ng-controller="Ctrl1">
<h1>{{value}}</h1>
</div>
</div>
http://jsfiddle.net/pbxaD/49/
if you want to use $timeout you have to inject it. But why don't you just call the update method in a certain interval?
.controller('todaymatches', function($rootScope,$scope, $http, $interval) {
var update = function() {
$http.get("http://www.domaine.com/updatedjson/")
.success(function (response) {
$scope.matches = response;
});
};
var initialize = function() {
$interval(function() {
update();
}, 60 * 1000)
};
initialize();
})
I tried this for the count down and it seems to work.
app.controller('CountDownController', function($scope, $timeout) {
$scope.counter = 60;
$scope.countdown = function() {
if ($scope.counter === 0) {
// do your reload and execute here
//Just reset the counter if you just want it to count again
$scope.counter = 60;
return;
} else {
$timeout(function() {
console.log($scope.counter);
$scope.counter--;
$scope.countdown();
}, 1000);
}
};
$scope.countdown();
});
You could tie up the various things you want to do inside the if condition of the above code as commented. I just reset the counter after counting down to 0.
I coded the below directive for infinite scroll, my problem which I couldn't figure out why it just fire once when the directive is loaded, I need your advice on how to make my list infinite-scroll.
I'm using it to get data remotely and each time i'm calling it I add to the counter 25, so each time it would return more data.
Thanx,
angular.module('MyApp')
.controller('InboxCtrl', function($scope, InboxFactory) {
var counter = 0;
$scope.loadData = function() {
var promise = InboxFactory.getEvents(counter);
promise.then(function(result) {
$scope.events = result;
});
counter += 25;
};
});
angular.module('MyApp')
.factory('InboxFactory', function($http, $q) {
// Service logic
var defered = $q.defer();
function getUrl(count) {
return "api/inbox/get?request={'what':'Search','criteria':'inbox','criteriaId':null,'startTime':null,'endTime':null,'offset':" + count + ",'limit':25,'order':'event_time','direction':'DESC','source':''}";
}
function extract(result) {
return result.data.data;
}
// Public API here
return {
getEvents: function(count) {
$http.get(getUrl(count)).then(
function(result) {
defered.resolve(extract(result))
}, function(err) {
defered.reject(err);
}
);
return defered.promise;
}
};
});
angular.module('MyApp')
.directive('infiniteScroll', ['$timeout',
function(timeout) {
return {
link: function(scope, element, attr) {
var
lengthThreshold = attr.scrollThreshold || 50,
timeThreshold = attr.timeThreshold || 400,
handler = scope.$eval(attr.infiniteScroll),
promise = null,
lastRemaining = 9999;
lengthThreshold = parseInt(lengthThreshold, 10);
timeThreshold = parseInt(timeThreshold, 10);
if (!handler || !components.isFunction(handler)) {
handler = components.noop;
}
element.bind('scroll', function() {
var
remaining = element[0].scrollHeight - (element[0].clientHeight + element[0].scrollTop);
//if we have reached the threshold and we scroll down
if (remaining < lengthThreshold && (remaining - lastRemaining) < 0) {
//if there is already a timer running which has no expired yet we have to cancel it and restart the timer
if (promise !== null) {
timeout.cancel(promise);
}
promise = timeout(function() {
handler();
promise = null;
}, timeThreshold);
}
lastRemaining = remaining;
});
}
};
}
]);
<ul class="inbox-list" infinite-scroll="loadData()">
<li class="clearfix" ng-repeat="event in events">{{event}}</li>
</ul>
I Made some changes the more important is the use of ng-transclude and the creation of a new scope for the directive to pass the method and the parameters. You can have a look at the jsbind. Of course the data are hard coded so i could fake the behaviour.
<ul class="inbox-list" my-infinite-scroll composite-method="loadData()">
I wrote as provider in application as below;
module.provider('CalculateTime', function() {
this.$get = function() {
var seconds = this.seconds;
var minutes = this.minutes;
var hours = this.hours;
var day = this.day;
return {
toSeconds: function(){
return Math.round(seconds);
},
toMinutes: function(){
return Math.round(minutes);
},
toHours: function(){
return Math.round(hours);
},
toDays: function(){
return Math.round(day);
},
exactDate: function(){
return Math.floor(hours%24)+":"+ Math.floor(minutes%60)+":"+ Math.floor(seconds%60);
}
}
};
this.setTime = function(milis) {
this.milis = milis;
this.seconds = this.milis/1000;
this.minutes = this.seconds/60;
this.hours = this.minutes/60;
this.day = this.hours/24;
};
});
and I want to set config inside of controller ( after finished some preparation and set it up). So I tried as follows;
module.controller('ResultController',function($scope,$data,CalculateTime){
var date = $data.post.date;
given.setDate(date.selectedYear,date.selectedMonth,date.selectedDay);
var now = new DateTime();
var diff = now.compare(given);
// config here
module.config(function(CalculateTimeProvider){
CalculateTimeProvider.setTime(diff);
});
setTimeout(function() {
$scope.$apply(function() {
$scope.result = CalculateTime.toDays() + " Days " + CalculateTime.exactDate();
});
}, 100);
$scope.result is become null and nothing come out. I know that I'm wrong in usage and still don't know correct way to use whether service or provider or factory. Please tell me about correct ways of setting and retrieving.
You can not configure a module after the bootstrapping phase. Configuration needs to happen BEFORE. You have to do this outside of your controller's scope.
I created a countdown clock as part of a larger project. Here is the code for the service
'use strict';
angular.module('myApp')
.service('Countdownclock', function Countdownclock() {
var secondsRemaining = 0;
var timerProcess;
return {
//initialize the clock
startClock: function(minutes) {
secondsRemaining = minutes * 60;
timerProcess = setInterval(this.timer, 1000);
},
//timer function
timer: function() {
secondsRemaining -= 1;
if (secondsRemaining <= 0) {
clearInterval(timerProcess);
}
},
//get time
getTime: function() {
return secondsRemaining;
},
//add time
addTime: function(seconds) {
secondsRemaining += seconds;
},
//stop clock
stopClock: function() {
secondsRemaining = 0;
clearInterval(timerProcess);
}
};
});
Then I call it to a from a controller which is also linked to a view
'use strict';
angular.module('myApp')
.controller('MainCtrl', function($scope, Countdownclock) {
Countdownclock.startClock(1);
$scope.seconds = Countdownclock.getTime();
$scope.$watch(Countdownclock.getTime(), function(seconds) {
$scope.seconds = Countdownclock.getTime();
});
});
For some reason I can't figure out how to bind secondsRemaining to $scope.seconds. I've been trying to figure this thing out for about an hour. I'm not exactly a wiz at functional programing so I have a feeling I'm just thinking about it wrong.
Inject $interval into your service and replace setInterval with it:
timerProcess = $interval(this.timer, 1000);
If you want to use a watcher you can register it like this:
$scope.$watch(function () { return Countdownclock.getTime(); }, function (newValue, oldValue) {
// Might be identical when called due to initialization - Good to know for some cases
if (newValue !== oldValue) {
$scope.seconds = newValue;
}
});
Demo: http://plnkr.co/edit/usUoOtWMwoDRht27joOA?p=preview
You can use a function instead:
$scope.seconds = function() { return Countdownclock.getTime() };
Then remove the
$scope.$watch(Countdownclock.getTime(), function(seconds) {
$scope.seconds = Countdownclock.getTime();
});
You can then use it in your template like this:
<div>{{seconds()}}</div>
But first, like Spock said, you have to use $interval instead of setInterval.