Angular Directive on Binded Values - javascript

Im trying to make a text formatting Directive in Angular.
In my current application i have many places where i have to present time in HH:MM:SS format, the time in the DB is saved in seconds. So i was thinking that maybe a directive would be the right thing here.
For example: everywhere i want to present time in HH:MM:SS i just add the directive "showAsTime" to the element containing the time in seconds.
Q1 What would be the best approach here?
Only part of the code, shown:
<table>
<tr data-ng-repeat="time in timeList">
<td data-show-as-time>{{ time.rowTime }}</td> //time in seconds
<tr>
</table>
The problem is that i dont get the value, the directive is executed first, if i "hardcode" a value say: 900 in place of {{ time.rowTime }} i get the correct presentation "00:15:00".
Q2 In one part of the application i have a model bound to a counter as shown below.
Is it possible to make the same Directive work even here?
Only part of the code, shown:
<h1 data-ng-model="timeCounter" data-show-as-time>{{timeCounter}}</h1>
//timeCounter adds 1 to timeCounter every second (with a $interval in the controller).
Time calculations is done as follows,
time.js
app.factory('time',function(){
var timeFactory = {};
timeFactory.timeFromSeconds = function(seconds){
var timeFromSeconds
var minutes = Math.floor(seconds/60);
var hours = Math.floor(seconds/3600);
if(hours > 0) minutes = minutes - hours * 60;
seconds = seconds - hours * 3600 - minutes * 60;
if(hours < 10) hours = "0" + hours;
if(minutes < 10) minutes = "0" + minutes;
if(seconds < 10) seconds = "0" + seconds;
timeFromSeconds = hours +":"+minutes+":" + seconds;
return timeFromSeconds;
}
return timeFactory;
}

There is already a date filter in Angular that can do this for you.
Check the link out here: https://docs.angularjs.org/api/ng/filter/date

Besides the date filter stated in the other answer (https://docs.angularjs.org/api/ng/filter/date), you could also implement a custom filter to further format your model data:
angular.filter('doSomething', function() {
return function(input) {
//do something on your input and return it
}
})
In your html, then, you can call your filter as
{{model|doSomething}}
This is probably the best way to obtain the behavior you want, rather than using a directive.

Related

Vee Validate custom cross-field rule, comparing two time strings

I have two time strings in my buefy form in vue that i want to validate the second one according to the first one, to only input no more than a one hour difference. I have the fields granularity to miliseconds.
my script
import { Validator } from 'vee-validate';
//Cross-field Rules
Validator.extend('isOneHour', (value, [otherValue]) => {
function toSeconds(time_str) {
// Extract hours, minutes and seconds
var parts = time_str.split(':');
var mili = time_str.split('.')
// compute and return total seconds
return parts[0] * 3600 + // an hour has 3600 seconds
parts[1] * 60 + // a minute has 60 seconds
+parts[2] // seconds
+ mili[0] / 1000; //miliseconds
}
console.log(value, otherValue); // out
var difference = Math.abs(toSeconds(value) - toSeconds(otherValue));
return difference <= 3600;
}, {
hasTarget: true
});
my template:
<b-input
#keyup.native.enter="getData()"
editable
:value="startTime"
#change.native="startTime = $event.target.value"
placeholder="ex. 11:22:00.000"
icon="clock"
v-mask="'##:##:##.###'"
name="startTime"
ref="endTime"
></b-input>
<b-input
editable
name="endTime"
:value="endTime"
#change.native="endTime = $event.target.value"
placeholder="ex. 11:25:30.450"
icon="stopwatch"
#keyup.native.enter="getData()"
v-mask="'##:##:##.###'"
v-validate="'isOneHour:endTime'"
></b-input>
this code does not work, it will create an endless loop, which will cause the app to crash. it works before the:
var difference = Math.abs(toSeconds(value) - toSeconds(otherValue));
my console error is: TypeError: time_str.split is not a function
what am I doing wrong here?
So! the problem was that my events on the Vee Validate was set to "" and after I set it to "blur" the validation triggered.
Although the error was coming from error.first('symbol_input) which according to my errorBagName should have been vErrors.first('symbol_input').
If anybody gets stuck on this like me maybe this answer will be where they went wrong.

"infinite" setinterval function "timing out"

having a slightly weird issue that I cant figure out. Ive set up a javascript timer, all it does is repeats an interval every second that checks the difference between 2 dates and displays the results. All seems fine, however when leaving the browser open for several minutes (not touching it.. literally walking away for a while), it seems to "time out" and stop functioning. No console error messages or anything, the code just stops executing.. Was wondering if anyone had any idea what could be causing this? Is my code the issue or is this a built in browser function to stop js functions if there is no input from the user on a page for a certain time?
edit sorry should mention this timer is set to run for around 40 days at the moment so it will never realistically meet the clearinterval statement in a user session. The future date variable im adding to the function is a dynamic unix timestamp from PHP for a date which is roughly 40 days in future. Currently set to 1444761301.88
function MModeTimer(futureDate) {
zIntervalActive = true;
var currentTime = new Date().getTime() / 1000;
var timeRemaining = futureDate - currentTime;
var minute = 60;
var hour = 60 * 60;
var day = 60 * 60 * 24;
var zDays = Math.floor(timeRemaining / day);
var zHours = Math.floor((timeRemaining - zDays * day) / hour);
var zMinutes = Math.floor((timeRemaining - zDays * day - zHours * hour) / minute);
var zSeconds = Math.floor((timeRemaining - zDays * day - zHours * hour - zMinutes * minute));
if (zSeconds <= 0 && zMinutes <= 0) {
console.log("timer in negative");
// timer at zero
clearInterval(zTimeInterval);
} else {
if (futureDate > currentTime) {
console.log("timer interval running");
// changes html as part of function
}
}
}
zTimeInterval = setInterval(function() {
MModeTimer(zNewTime)
}, 1000);
This line:
clearInterval(zTimeInterval);
Is clearing the interval when the condition:
if (zSeconds <= 0 && zMinutes <= 0) {
Is met.
And as per the log you've wrote inside, that would be wrong. You are checking that zSeconds and zMinues are less or equal to 0. So when both are 0, the interval will be cleared.
Edit
As per your edits and explanations, may I suggest adding a console log that i'ts not inside any condition?:
function MModeTimer(futureDate) {
console.log('running');
//... rest of your code
That way you can make sure if the interval is running, maybe your conditions are not being TRUE after a while and you won't see any log, but the interval would be still running.

Multiple countDown timers on a single page in a meteor app

I am struggling to get multiple countdown timers displayed in a meteor app.
AuctionExpireIn is a date in the format - 2015-03-23T17:17:52.412Z.
Have a auctionItems collection , displaying 3 rows of auction items in auctionItem template.
While the target date is different for each of the auction items, what I am seeing is all the three rows have the same countdown timer . Apparently the session is not tied to each of the records and the same session value is being displayed for all the three rows.
How do I get different rows to display countdown timer based on the target data that each auction item document has?
Appreciate all the help.
Template.auctionItem.helpers({
AuctionExpireIn : function() {
var target_date = new Date(this.AuctionExpireIn).getTime();
//alert (target_date);
// variables for time units
var days, hours, minutes, seconds;
// update the tag with id "countdown" every 1 second
setInterval(function( ) {
// find the amount of "seconds" between now and target
var current_date = new Date().getTime();
var seconds_left = (target_date - current_date) / 1000;
var countdown ='';
// do some time calculations
days = parseInt(seconds_left / 86400);
seconds_left = seconds_left % 86400;
hours = parseInt(seconds_left / 3600);
seconds_left = seconds_left % 3600;
minutes = parseInt(seconds_left / 60);
seconds = parseInt(seconds_left % 60);
// format countdown string + set tag value
countdown= days + "d, " + hours + "h, " + minutes + "m, " + seconds + "s";
Session.set ('countdown', countdown);
}, 1000);
return Session.get('countdown');
}
});
You can tie each countdown timer to an instance of your auctionItem template. The below works as a proof-of-concept; each template will pick a random number from 1 to 10 and count down to 0. To apply it to your case, just replace the random number with the moment.js difference between this.AuctionExpireIn and new Date() (which I did not look up because I don't know it offhand).
Template.auctionItem.created = function(){
var self = this;
var num = Math.floor(Math.random() * 10);
this.remaining = new ReactiveVar(num);
this.interval = Meteor.setInterval(function(){
var remaining = self.remaining.get();
self.remaining.set(--remaining);
if (remaining === 0){
Meteor.clearInterval(self.interval);
}
}, 1000);
}
Template.auctionItem.helpers({
remaining: function(){
return Template.instance().remaining.get();
}
});
One point I noticed from the efforts above is you don't want to try to set the counters from a helper; just get the counters from a helper.
You can do this in a spacebars helper using ReactiveVar or Session. This example outputs the current time every second.
Template.registerHelper('Timer', (options)->
now = new Date()
reactive = new ReactiveVar(now.getTime())
Meteor.setInterval(()->
now = new Date()
reactive.set now.getTime()
,1000)
return reactive.get()
)
I'm not sure if the above is all of your current logic for your application, but based on what you have provided, the issue seems to be that you are using a single Session variable to keep track of the remaining time for three different auction items. If you need to use Session in order to keep track of your remaining times for the three different auction items, you should use three separate Session variables to do so, for example:
Session.set('auctionItemOne');
Session.set('auctionItemTwo');
Session.set('auctionItemThree');
But honestly, this is not the best way to go about solving this problem. Instead, I would recommend creating a simple template helper that takes in the end date/time for an auction item and outputs the appropriate string to describe the amount of time remaining. This can be easily accomplished with a library such as Moment.js. In order to implement this solution, do something like the following:
// Template
<template name="auctionItem">
{{timeRemaining}}
</template>
// Template Helper
Template.auctionItem.helpers({
timeRemaining: function() {
// Call appropriate Moment.js methods
// Return the string returned by Moment.js
}
});
I hope that this helps point you in a better direction for how to handle your situation. Please let me know if you need any further explanation or assistance.

using the time as a dynamic variable to compare values

I am working on programming a page in JS that grabs calendar data from an outside source, imports it into a multidimensional array and uses it to display who is currently working along with their photo, phone number, etc.
Right now I have it set up so that the page reloads every 15 minutes. I'd prefer to have this all done dynamically so that when, say, the clock strikes 5pm the page knows to update without having to wait until the 15 minute refresh is triggered.
All of the work times are pulled from the other calendar in 24 hour format (so 5pm is 1700).
Here's how I'm generating the current time to compare with the start/end times in the calendar:
//Get the current date and time
var dateTime = new Date();
var month = dateTime.getMonth() + 1;
var day = dateTime.getDate();
var dayOfWeek = dateTime.getDay();
var year = dateTime.getYear() + 1900;
//converting hours and minutes to strings to form the 24h time
var hours = dateTime.getHours().toString();
if (hours.length === 1) {
var hours = '0' + hours
};
var minutes = dateTime.getMinutes().toString();
if (minutes.length === 1) {
var minutes = '0' + minutes
};
var time = hours + minutes;
//convert the 24h time into a number to read from later
var timeNumber = parseInt(time);
I then use if statements to compare the start/end times from the imported schedule with timeNumber to determine who is currently working and push that to an array that is eventually displayed on the page with this code:
//figure out who is currently working and put them in the workingNow array
var workingNow = [];
for (i = 0; i < workingToday.length; i++){
//convert time strings to numbers to compare
var startTime = parseInt(workingToday[i][7]);
var endTime = parseInt(workingToday[i][8]);
//compare start and end times with the current time and add those who are working to the new list
if(startTime < timeNumber && timeNumber < endTime){
workingNow.push(workingToday[i]);
}
};
I guess I have just been trying to figure out how to make this comparison of the data in an array with the current time something that is dynamic. Is this possible or would I need to go about this in a completely different way from the ground up?
You should have a look at momentjs. This is a really good library to handle all sort of time and date manipulation.
http://momentjs.com/

Android-Esque Time Entry Widget doesn't work when minutes section is below ten

I'm trying to make a "widget" used for controlled time entry that uses up and down arrows to increment each section in addition to allowing you to type the values in by hand. It worked fine(looping over from 12 to 1 and 59 to 1 ect.), but when I added a section that formatted the minutes properly when the value was below 10, it started acting up.
Pressing down acts normally until 10 where it displays, "10" then "09" then "0-1".
Pressing up after you get to "0-1" goes to "01" but when you get to "08", it goes back to "01".
function minFix(mins)
{
if ( mins <= 9)
{
mins = "0" + String(mins);
document.getElementById("mins").value = mins;
}
else
{
document.getElementById("mins").value = mins;
}
}
function minUp()
{
var mins = parseInt(document.getElementById("mins").value);
if (mins == 59)
{
mins = 1;
minFix(mins);
}
else
{
mins = (mins+1);
minFix(mins);
}
}
function minDown()
{
var mins = parseInt(document.getElementById("mins").value);
if (mins == 1)
{
mins = 59;
minFix(mins);
}
else
{
mins = (mins-1);
minFix(mins);
}
}
The minUp and minDown are called by the up and down arrows respectively and the textbox that displays mins has an ID of mins.
I'm sure I'm missing something simple but I can't figure it out right now so any help would be much appreciated.
this has to do with your zero that you're appending. It makes your number into a octal number, so when you read it back in, it's not being read as a decimal number. Take a look at how parseInt works and you'll understand. You need to remove the leading zero before parsing.
Scroll down for the parseInt function here: http://www.javascripter.net/faq/convert2.htm
so this line var mins = parseInt(document.getElementById("mins").value);
should be
var mins = parseInt(document.getElementById("mins").value.replace(/^0+/,""));
Oh, and welcome to Stack! please don't forget to up-vote the answers that you feel are best, and give the green checkmarks to those that you feel are the correct ones.

Categories

Resources