I have some code in my controller, but the code is reusable.. So I would like to move it in a factory and then use the factory everytime I need it... I am not able to move this code to the factory.. If I move it, nothing works anymore. Here the code I have in my controller and that I would like to move in the factory:
var app = angular.module("clock.app");
app.controller('timer',['$scope','$interval','$timeout','timerFactory',
function($scope, $interval,$timeout,timerFactory){
var framework7App = new Framework7();
var $$ = Dom7;
$scope.timeList = [
{"hour":0, "minutes":1, "seconds": 6},
{"hour":0, "minutes":3, "seconds": 180},
{"hour":0, "minutes":5, "seconds": 300}];
var today = new Date();
var arr,hour, minutes, seconds,convertedSec;
var getStoredList = JSON.parse(localStorage.getItem("timeListDetails"));
if(getStoredList !=null){
if(getStoredList.length != 0){
$scope.timeList = getStoredList;
}else{
localStorage.setItem("timeListDetails", JSON.stringify($scope.timeList));
}
}else{
getStoredList = $scope.timeList;
}
$scope.timerWithInterval = 0;
$scope.startTimerWithInterval = function() {
$scope.timerWithInterval = 0;
if($scope.myInterval){
$interval.cancel($scope.myInterval);
}
$scope.onInterval = function(){
$scope.timerWithInterval++;
}
$scope.myInterval = $interval($scope.onInterval,1000);
};
$scope.resetTimerWithInterval = function(){
$scope.timerWithInterval = 0;
$interval.cancel($scope.myInterval);
}
$scope.timeCounterInSeconds= function(seconds) {
$scope.startTimerWithInterval();
$timeout(function () {
$scope.timeCounter(seconds)
}, 1000);
};
$scope.timeCounter = function(seconds) {
if($scope.timerWithInterval==seconds) {
$scope.resetTimerWithInterval();
framework7App.alert('Time Over','');
}
else {
$timeout(function () {
$scope.timeCounter(seconds)
}, 1000);
}
};
$scope.submit = function() {
$scope.timeList.push({"hour":hour,
"minutes":minutes,
"seconds":seconds,
"convertedSec":convertedSec,
"timeFlag": true});
localStorage.setItem("timeListDetails", JSON.stringify($scope.timeList));
$scope.hidePopup();
};
$scope.displayPopup = function(){
$scope.popupAddTimer = true;
}
$scope.hidePopup = function(){
$scope.popupAddTimer = false;
}
timerFactory.picker();
}]);
I have used below factory method:
var factoryApp = angular.module('clock.factories');
factoryApp.factory('timerFactory',[
function() {
var timerFactory = {};
var framework7App = new Framework7();
var $$ = Dom7;
var today = new Date();
var arr,hour, minutes, seconds,convertedSec;
timerFactory.picker = function() {
framework7App.picker({
input: '#picker-date',
container: '#picker-date-container',
toolbar: false,
rotateEffect: true,
value: [today.getHours(), (today.getMinutes() < 10 ? '0' + today.getMinutes() : today.getMinutes()), today.getSeconds()],
onOpen: function(p){
},
formatValue: function (p, values, displayValues) {
arr = displayValues[0] + ':' + values[1] + ':' +values[2];
hour = displayValues[0];
var arrVal = arr.split(":");
convertedSec = (+arrVal[0] * 60 * 60 +(arrVal[1]) *60 +(+arrVal[2]));
minutes = values[1];
seconds = values[2];
return arr;
},
cols: [
// Hours
{
values: (function () {
var arr = [];
for (var i = 0; i <= 23; i++) { arr.push(i); }
return arr;
})(),
},
// Divider
{
divider: true,
content: ':'
},
// Minutes
{
values: (function () {
var arr = [];
for (var i = 0; i <= 59; i++) { arr.push(i < 10 ? '0' + i : i); }
return arr;
})(),
},
// Divider
{
divider: true,
content: ':'
},
// Seconds
{
values: (function () {
var arr = [];
for (var i = 0; i <= 59; i++) { arr.push(i < 10 ? '0' + i : i); }
return arr;
})(),
},
]
});
}
return timerFactory;
}]);
Successfully called picker method but unable to write another methods ($scope methods) in factory. Can anyone please guide me how to do that, as I am new to angularJS.
Also let me know, how to use variables from factory (i.e hour,seconds, minutes) into the controller?
It also not allowing me to use $scope and $interval.
no its not allowed $interval as well
To use the $interval service in a factory, simply inject it in the factory construction function:
app.factory("timerFactory", function($interval) {
//inject here ^^^^^^^^^^
var timer = {};
timer.count = 0;
var intervalPromise;
timer.start = function() {
if (intervalPromise) return;
intervalPromise = $interval(()=>(timer.count++), 1000);
};
timer.stop = function() {
$interval.cancel(intervalPromise);
intervalPromise = null;
};
return timer;
});
The DEMO on JSFiddle.
I have written this code to get the value of the Timer in a div :
var timerGenerator = (function() {
var time = {
centiSec: 0,
secondes: 0,
minutes: 0,
hours: 0
};
var container = document.createElement("div");
var timeDisplay = function() {
function smoothDisplay(value) {
return (value < 10 ? '0' : '') + value;
}
return "" + smoothDisplay(this.hours) + ":" + smoothDisplay(this.minutes) + ":" + smoothDisplay(this.secondes) + "." + smoothDisplay(this.centiSec);
};
var boundTimeDisplay = timeDisplay.bind(time);
var timeUpdate = function() {
container.innerHTML = boundTimeDisplay();
//console.log(container);
this.centiSec++;
if(this.centiSec === 100) {
this.centiSec = 0;
this.secondes++;
}
if(this.secondes === 60) {
this.secondes = 0;
this.minutes++;
}
if(this.minutes === 60) {
this.minutes = 0;
this.hours++;
}
};
var boundTimeUpdate = timeUpdate.bind(time);
window.setInterval(boundTimeUpdate,10);
console.log(container);
return container;
})();
This code works when I link the var timerGenartor with a div. But, now I would like to return the var container into a string. So I changed my code to this : (I just changed the initialization of container and this value)
var timerGenerator = (function() {
var time = {
centiSec: 0,
secondes: 0,
minutes: 0,
hours: 0
};
var container = "";
var timeDisplay = function() {
function smoothDisplay(value) {
return (value < 10 ? '0' : '') + value;
}
return "" + smoothDisplay(this.hours) + ":" + smoothDisplay(this.minutes) + ":" + smoothDisplay(this.secondes) + "." + smoothDisplay(this.centiSec);
};
var boundTimeDisplay = timeDisplay.bind(time);
var timeUpdate = function() {
container = boundTimeDisplay();
//console.log(container);
this.centiSec++;
if(this.centiSec === 100) {
this.centiSec = 0;
this.secondes++;
}
if(this.secondes === 60) {
this.secondes = 0;
this.minutes++;
}
if(this.minutes === 60) {
this.minutes = 0;
this.hours++;
}
};
var boundTimeUpdate = timeUpdate.bind(time);
window.setInterval(boundTimeUpdate,10);
console.log(container);
return container;
})();
With this modification nothing is returned or display in the console and I don't understand why. However, the comment "console.log" gives me good the timer. So, why the first console.log displays the good result and not the second one ?
The working code as you expected with two JS functions
// JS 1
var timerGenerator = (function() {
var time = {
centiSec: 0,
secondes: 0,
minutes: 0,
hours: 0
};
var container = "";
var timeDisplay = function() {
function smoothDisplay(value) {
return (value < 10 ? '0' : '') + value;
}
return "" + smoothDisplay(this.hours) + ":" + smoothDisplay(this.minutes) + ":" + smoothDisplay(this.secondes) + "." + smoothDisplay(this.centiSec);
};
var boundTimeDisplay = timeDisplay.bind(time);
var timeUpdate = function() {
container = boundTimeDisplay();
//console.log(container);
this.centiSec++;
if(this.centiSec === 100) {
this.centiSec = 0;
this.secondes++;
}
if(this.secondes === 60) {
this.secondes = 0;
this.minutes++;
}
if(this.minutes === 60) {
this.minutes = 0;
this.hours++;
}
return container;
};
var boundTimeUpdate = timeUpdate.bind(time);
return boundTimeUpdate;
})();
// JS 2
var spanGenerator = (function() {
var container = document.createElement('span');
window.setInterval(function(){
container.innerHTML = timerGenerator();
}, 10);
return container;
})();
document.getElementById('timer').appendChild(spanGenerator);
<div id="timer"></div>
It can be because your container is empty as it is initialized in timeUpdate function which is eventually called 10ms later(for the first time) because of the setInterval. U will have to call the timeUpdate function before putting it in setInterval.
I am building a custom mp3 player for a entertainment web site and i need to add songs continuously even when playing a song. to do that i'm using this function.
var temp = [];
var tempObj = [];
function popUp(file,thumb,trackName,trackArtist,trackAlbum) {
var validate = true;
if (temp.length>0) {
for (var x = 0; x < temp.length; x++) {
if (temp[x]['trackName'] == trackName) {
validate = false;
}
}
}
if (validate==true){
// Save data to object
tempObj = { file: file, thumb: thumb, trackName: trackName, trackArtist: trackArtist, trackAlbum: trackAlbum };
temp.push(tempObj); // push object to existing array
$("#player").jAudio({playlist: temp});
}
}
Problem is that to add songs to player we need to run that "jAudio()" function. because of that every time "popUp()" function called it calling the "jAudio()" function. if any one have a solution, please shear it..
This is the JAudio API.
!function (t) {
function i(i, a) {
this.settings = t.extend(!0, r, a), this.$context = i, this.domAudio = this.$context.find("audio")[0], this.$domPlaylist = this.$context.find(".jAudio--playlist"), this.$domControls = this.$context.find(".jAudio--controls"), this.$domVolumeBar = this.$context.find(".jAudio--volume"), this.$domDetails = this.$context.find(".jAudio--details"), this.$domStatusBar = this.$context.find(".jAudio--status-bar"), this.$domProgressBar = this.$context.find(".jAudio--progress-bar-wrapper"), this.$domTime = this.$context.find(".jAudio--time"), this.$domElapsedTime = this.$context.find(".jAudio--time-elapsed"), this.$domTotalTime = this.$context.find(".jAudio--time-total"), this.$domThumb = this.$context.find(".jAudio--thumb"), this.currentState = "pause", this.currentTrack = this.settings.defaultTrack, this.timer = void 0, this.init()
}
function a(t, i) {
for (var t = String(t); t.length < i;)t = "0" + t;
return t
}
var e = "jAudio", r = {
playlist: [],
defaultAlbum: void 0,
defaultArtist: void 0,
defaultTrack: 0,
autoPlay: !1,
debug: !1
};
i.prototype = {
init: function () {
var t = this;
t.renderPlaylist(), t.preLoadTrack(), t.highlightTrack(), t.updateTotalTime(), t.events(), t.debug(), t.domAudio.volume = .2
}, play: function () {
var t = this, i = t.$domControls.find("#btn-play");
t.currentState = "play", t.domAudio.play(), clearInterval(t.timer), t.timer = setInterval(t.run.bind(t), 50), i.data("action", "pause"), i.attr("id", "btn-pause"), i.toggleClass("active")
}, pause: function () {
var t = this, i = t.$domControls.find("#btn-pause");
t.domAudio.pause(), clearInterval(t.timer), t.currentState = "pause", i.data("action", "play"), i.attr("id", "btn-play"), i.toggleClass("active")
}, stop: function () {
var t = this;
t.domAudio.pause(), t.domAudio.currentTime = 0, t.animateProgressBarPosition(), clearInterval(t.timer), t.updateElapsedTime(), t.currentState = "stop"
}, prev: function () {
var t, i = this;
t = 0 === i.currentTrack ? i.settings.playlist.length - 1 : i.currentTrack - 1, i.changeTrack(t)
}, next: function () {
var t, i = this;
t = i.currentTrack === i.settings.playlist.length - 1 ? 0 : i.currentTrack + 1, i.changeTrack(t)
}, preLoadTrack: function () {
var t = this;
t.changeTrack(t.settings.defaultTrack), t.settings.autoPlay && t.play()
}, changeTrack: function (t) {
var i = this;
i.currentTrack = t, i.domAudio.src = i.settings.playlist[t].file, i.highlightTrack(), i.updateThumb(), i.renderDetails(), "play" === i.currentState && i.play()
}, events: function () {
var i = this;
i.$domControls.on("click", "button", function () {
var a = t(this).data("action");
switch (a) {
case"prev":
i.prev.call(i);
break;
case"next":
i.next.call(i);
break;
case"pause":
i.pause.call(i);
break;
case"stop":
i.stop.call(i);
break;
case"play":
i.play.call(i)
}
}), i.$domPlaylist.on("click", ".jAudio--playlist-item", function () {
var a = t(this), e = (a.data("track"), a.index());
i.currentTrack !== e && i.changeTrack(e)
}), i.$domProgressBar.on("click", function (t) {
i.updateProgressBar(t), i.updateElapsedTime()
}), t(i.domAudio).on("loadedmetadata", function () {
i.animateProgressBarPosition.call(i), i.updateElapsedTime.call(i), i.updateTotalTime.call(i)
})
}, getAudioSeconds: function (t) {
var t = t % 60;
return t = a(Math.floor(t), 2), t = 60 > t ? t : "00"
}, getAudioMinutes: function (t) {
var t = t / 60;
return t = a(Math.floor(t), 2), t = 60 > t ? t : "00"
}, highlightTrack: function () {
var t = this, i = t.$domPlaylist.children(), a = "active";
i.removeClass(a), i.eq(t.currentTrack).addClass(a)
}, renderDetails: function () {
var t = this, i = t.settings.playlist[t.currentTrack], a = (i.file, i.thumb, i.trackName), e = i.trackArtist, r = (i.trackAlbum, "");
r += "<p>", r += "<span>" + a + "</span>", r += "<span>" + e + "</span>", r += "</p>", t.$domDetails.html(r)
}, renderPlaylist: function () {
var i = this, a = "";
t.each(i.settings.playlist, function (t, i) {
{
var e = i.file, r = i.thumb, o = i.trackName, s = i.trackArtist;
i.trackAlbum
}
trackDuration = "00:00", a += "<div class='jAudio--playlist-item' data-track='" + e + "'>", a += "<div class='jAudio--playlist-thumb'><img src='" + r + "'></div>", a += "<div class='jAudio--playlist-meta-text'>", a += "<h4>" + o + "</h4>", a += "<p>" + s + "</p>", a += "</div>", a += "</div>"
}), i.$domPlaylist.html(a)
}, run: function () {
var t = this;
t.animateProgressBarPosition(), t.updateElapsedTime(), t.domAudio.ended && t.next()
}, animateProgressBarPosition: function () {
var t = this, i = 100 * t.domAudio.currentTime / t.domAudio.duration + "%", a = {width: i};
t.$domProgressBar.children().eq(0).css(a)
}, updateProgressBar: function (t) {
var i, a, e, r = this;
t.offsetX && (i = t.offsetX), void 0 === i && t.layerX && (i = t.layerX), a = i / r.$domProgressBar.width(), e = r.domAudio.duration * a, r.domAudio.currentTime = e, r.animateProgressBarPosition()
}, updateElapsedTime: function () {
var t = this, i = t.domAudio.currentTime, a = t.getAudioMinutes(i), e = t.getAudioSeconds(i), r = a + ":" + e;
t.$domElapsedTime.text(r)
}, updateTotalTime: function () {
var t = this, i = t.domAudio.duration, a = t.getAudioMinutes(i), e = t.getAudioSeconds(i), r = a + ":" + e;
t.$domTotalTime.text(r)
}, updateThumb: function () {
var t = this, i = t.settings.playlist[t.currentTrack].thumb, a = {"background-image": "url(" + i + ")"};
t.$domThumb.css(a)
}, debug: function () {
var t = this;
t.settings.debug && console.log(t)
}
}, t.fn[e] = function (a) {
var e = function () {
return new i(t(this), a)
};
t(this).each(e)
}
}(jQuery);
// initialize
(function () {
}());
This is old, but if you are willing to go a little nasty, I have a working workaround. Forgive me for the nasty approach, in the end though - it works.
We are gonna make use of window here to use as a global scope across the functions.
init: function()
{
var self = this;
self.renderPlaylist();
self.preLoadTrack();
self.highlightTrack();
self.updateTotalTime();
self.events();
self.debug();
self.domAudio.volume = 0.05;
window.jAudioCore = self; // This line returns the self
},
Now you can change the playlist accordingly, anytime, just by changing window.playlist global variable, everywhere in the code.
renderPlaylist: function()
{
var self = this,
template = "";
$.each(window.playlist, function(i, a) //added window.playlist as the parameter
Your renderPlaylist function should look the one above.
To test this here's a simple code for you to use :
(function(){
window.playlist = [
{
file: "http://192.168.1.99:5000/mpeg",
thumb: "https://i.ytimg.com/vi/Kd57YHWqrsI/mqdefault.jpg",
trackName: "Carnival Of Rust",
trackArtist: "Poets Of The Fall",
trackAlbum: "Single",
trackDuration:"04:06",
},
{
file: "http://127.0.0.1:5000/mpeg",
thumb: "https://i.ytimg.com/vi/Kd57YHWqrsI/mqdefault.jpg",
trackName: "Carnival Of Rust",
trackArtist: "Poets Of The Fall",
trackAlbum: "Single",
trackDuration:"04:06",
}
];
var t = {
playlist: [], // this is no longer needed, you can remove it from the source code later
debug:true
}
$(".jAudio").jAudio(t);
setTimeout(function(){
window.playlist.push({
file: "http://127.0.0.1:5000/mpeg",
thumb: "https://i.ytimg.com/vi/Kd57YHWqrsI/mqdefault.jpg",
trackName: "Carnival Of Rust",
trackArtist: "Poets Of The Fall",
trackAlbum: "Single",
trackDuration:"04:06",
});
window.jAudioCore.renderPlaylist(); // renders the playlists
window.jAudioCore.preLoadTrack(); // will preload the current track
window.jAudioCore.highlightTrack(); // will highlight in the css
},3000);
})();
As I said, in the end, it works dynamically without refresh. There might be some underlying bugs, I couldn't test much on this. Cheers.
I have a simple class below that starts and then updates a count every second. How would I go about adding functionality for it to listen for a specific value and then fire a callback?
function Counter() {
this.currentCount = 0;
}
Counter.prototype.start = function() {
setInterval(this.update, 1000);
};
Counter.prototype.when = function(value, callback) {
callback(value);
};
Counter.prototype.update = function() {
this.currentCount++;
};
In my mind it would work something like this.
var counter = new Counter();
counter.when(50, function(value) {
console.log('We arrived at ' + value + ', the requested value.');
});
counter.start();
This is just a nice homework, I'll do that for you ;) Please have a look on my solution:
function Counter() {
this.currentCount = 0;
this.conditions = [];
this.interval = undefined;
}
Counter.prototype.start = function() {
if (!this.interval) {
var that = this;
this.interval = setInterval(function () {
that.update();
}, 1000);
}
};
Counter.prototype.stop = function () {
if (this.interval) {
clearInterval(this.interval);
this.interval = undefined;
}
this.currentCount = 0;
};
Counter.prototype.when = function(value, callback) {
var that = this;
this.conditions.push(function () {
if (that.currentCount === value) {
callback.call(that, value);
}
});
};
Counter.prototype.update = function() {
this.currentCount++;
for (var i = 0, l = this.conditions.length; i < l; i++) {
var condition = this.conditions[i];
condition();
}
};
var counter = new Counter();
counter.when(50, function(value) {
console.log('We arrived at ' + value + ', the requested value.');
});
counter.when(60, function (value) {
console.log('Stop at ' + value + '!');
this.stop();
});
counter.start();
and it's fiddled!
Another answer here made a good argument in hiding private variables, but implemented it a bit too confused, so this is another way of doing it similar. Instead of shared prototype functions this is using instance functions. Some may say this needs more memory, but I don't believe it's significant, and allows to easily have privates in a real constructor function.
var Counter = function () {
var that = this, currentCount = 0,
conditions = [], interval;
var update = function () {
currentCount++;
for (var i = 0, l = conditions.length; i < l; i++) {
var condition = conditions[i];
condition();
}
};
this.start = function () {
if (!interval) {
interval = setInterval(function() {
update.call(that);
}, 1000);
}
};
this.when = function (value, callback) {
conditions.push(function () {
if (currentCount === value) {
callback.call(that, value);
}
});
};
this.stop = function () {
if (interval) {
clearInterval(interval);
interval = undefined;
}
currentCount = 0;
};
};
var counter = new Counter();
counter.when(50, function(value) {
console.log('We arrived at ' + value + ', the requested value.');
});
counter.when(60, function (value) {
console.log('Stop at ' + value + '!');
this.stop();
});
counter.start();
see it fiddled!
Notice also that in both examples, counter is instanceof Counter and Object,
whereas Counter is instanceof Function and Object (why I like to write so much code ;))
To support multiple whens:
Add an array of whens in your class:
function Counter() {
this.currentCount = 0;
this.whens = [];
}
Then let the when function push to that:
Counter.prototype.when = function(value, callback) {
this.whens.push({'time' : value, 'callback' : callback});
}
And check for these whens at update:
Counter.prototype.update = function() {
this.currentCount++;
for(var w in this.whens) {
if(this.currentCount == this.whens[w].time) {
this.whens[w].callback();
}
}
}
Try something more like:
function Counter(interval, val, func){
this.currentCount = 0;
setInterval(function(){
this.currentCount++;
if(this.currentCount === val)func();
}, interval);
}
var nc = new Counter(1000, 50, function(){
console.log('We have arrived at '+nc.currrentCount);
});
There is an argument to be made for something like this instead:
var Counter = (function() {
var update = function() {
var idx, callbacks;
this.currentCount++;
callbacks = this.callbacks[this.currentCount];
if (callbacks) {
for (idx = 0; idx < callbacks.length; idx++) {
callbacks[idx](this.currentCount);
}
}
};
var start = function() {
var counter = this;
setInterval(function() {update.call(counter)}, 1000);
};
var when = function(count, callback) {
(this.callbacks[count] || (this.callbacks[count] = [])).push(callback);
};
return function() {
var config = {currentCount: 0, callbacks: {}};
this.start = function() {return start.call(config);};
this.when = function(count, callback) {
return when.call(config, count, callback);
};
// this.stop = ... // if desired
};
}());
This is somewhat more memory intensive than the prototype-based version of the code. I wouldn't use it for something where you were expecting hundreds of thousands or millions of objects. But it has the advantage that it truly encapsulates the data you might like to keep hidden, such as currentCount and the list of callbacks. It doesn't expose an unnecessary update function. And it's not terribly more heavy than the prototype version. Each instance has its own start and when functions, but those are just thin wrappers around common functions.
It is a bit more difficult to add a stop function to this in the same manner, unless you don't mind exposing the the result of setInterval. But it is doable.