Convert Milliseconds to HH:mm:ss format in Angular - html

I'm trying to convert my milliseconds to readable Hour, minute, second format. I've tried to do this manually but I'm stuck, and I feel like there should be a simpler way.
Current manually written stopwatch:
ngOnInit(){
//alert(this.datePipe.transform(new Date()));
let timer = Observable.timer(0, 1000);
timer.subscribe(t=>{
this.today = this.datePipe.transform(t);
this.second = t;
if(this.second==60){
this.second = 0;
this.minute += 1;
}else if(this.minute == 60){
this.minute = 0;
this.hour += 1;
}
});
}
Once the t passes 60 sec, seconds wont reset and it will continue to be over 60.
So my question is, is there a simpler way that actually works? I've read this and I don't quite understand how it works in my example DatePipe
HTML
<div class="client-time">
<span>Client time</span>
<br/>
<strong>{{hour}}:{{minute}}:{{second}}</strong>
</div>
</footer>
Basically I'm trying to show how long a user has been on the page.

To use DatePipe in combination with time take a look at the following:
start = new Date(0,0,0);
ngOnInit() {
Observable.interval(1000)
.startWith(0)
.subscribe(t => {
this.start = new Date(0, 0, 0);
this.start.setSeconds(t);
});
}
And the HTML should be
{{start | date:'HH:mm:ss'}}

Here is a simple version, showing the time in seconds:
Component:
private timer;
private counter: Date;
ngOnInit() {
this.timer = Observable.timer(0,1000)
.subscribe(t => {
this.counter = new Date(0,0,0,0,0,0);
this.counter.setSeconds(t);
});
}
ngOnDestroy(){
this.timer.unsubscribe();
}
Template:
<div class="client-time">
<span>Client time</span><br/>
<strong>{{counter | date:'HH:mm:ss'}} seconds</strong>
</div>

Related

How to Run a loop in jQuery to reread defined array sets?

I'm trying to build a reoccurring logic using jQuery & Arrays, but running into issues with getting the code to rerun. I'd like the code to read the next array within the matrix once the user clicks "Next Button." Currently, the logic isn't progressing past the first array, but I'm not sure why! Any help is appreciated.
<body>
<div id="wordZone"></div>
<ul id="choices">
<li></li>
<li></li>
<li></li>
</ul>
Next Word
<div class="win">
You've Won!
</div>
<div class="lose">
You've Lost!
</div>
</body>
let score = 0;
const dict = {
officeSpeak: ["Hi there!", "Regards", "Per my last email"],
counter: 0,
};
const matrix = [
["Hi there!", "Sup dude", "Salutations"],
["Regards", "Hasta luego", "Byebye"],
["Per my last email","oopsie!","some other option here, you're writing this game"],
];
const wordZone = $("#wordZone");
const choiceButtons = $("#choices li");
function buildOptions() {
let turnChoices = matrix[0];
//hide next word button - DONE
$("#next").hide();
for (let i = 0, ii = turnChoices.length; i < ii; i++) {
let choiceWord = turnChoices[i];
let choiceButton = $(choiceButtons[i]);
let btnClass = "incorrect";
choiceButton.text(choiceWord);
if (dict.officeSpeak.indexOf(choiceWord) != -1) {
btnClass = "correct";
}
choiceButton.addClass(btnClass);
}
}
buildOptions();
function onClickWord(e) {
console.log($(this));
$("#choices li").addClass("active");
$(".correct").css("color", "green");
$(".incorrect").css("color", "red");
if ($(this).hasClass("correct")) {
score++;
console.log(score);
}
$("#next").show();
let turnChoices = matrix[+1];
}
$("#choices li").click(onClickWord);
$("#next").click(buildOptions);
function finalScore() {
$("#wordZone").show(score);
if (finalScore >= 2) {
$("#wordZone").addClass("win");
$("#win").show();
} else {
$("#wordZone").addClass("lose");
$("#lose").show();
}
}
finalScore();
//final score - HELP
I tried creating a for loop where the matrix counter should increment by 1 each time the program is ran, expecting that the code would then move onto the second line of the array.
It tooks a while to find a running way. Here my suggestion:
First: create a variable with global scope
let matrixcounter = 0;
Second: Add an argument to function buildOptions and pass it to your array matrix[]:
function buildOptions(wordCounter) {
let turnChoices = matrix[wordCounter];
...
}
This last change needs another important change, based on How can I pass arguments to event handlers in jQuery? :
So replace $("#next").click(buildOptions); with
$("#next").click(function() {
matrixcounter++; //means matrixcounter = matrixcounter + 1;
buildOptions(matrixcounter);
});
A running example: https://jsfiddle.net/reporter/rtqgveuo/1/

Using JQuery - How to show "Final Score" of a Quiz and Loop Through Questions Using an Array

I'm working on building a quiz using Jquery, Loops, and Arrays. I started with looping through the answers (seen in matrix). Now I'd like to do the same thing for questions so that each of the three options appears with a question/prompt as well. The matrix matches the dict object I have above, so I'm not sure how to replicate for questions...
Secondly, I'm having trouble getting a final score to show once we've reached the end of the arrays in the matrix. I've tried setting it so that the final score button appears once the matrix counter has reached an index beyond number of arrays, but it seems to be causing some issues. I also am having trouble returning the actual final score on the screen.
Any advice is helpful!
<body>
<div id="Start">
<div id="welcome" class="question">
<p>
Choose the right response. Don't get fired.
</p>
</div>
<button type="button" class="startBtn" id="startBtn">Start</button>
</div>
<div id="wordZone">
</div>
<ul id="choices">
<li></li>
<li></li>
<li></li>
</ul>
Next Word
Final Score
<div id="finalScore" class="hide">
<h3 id="scoreMessage"></h3>
<p id="playerScore"></p>
</div>
</body>
</html>
// setup
let score = 0;
let matrixcounter = 0;
const dict = {
officeSpeak: ["Hi there!", "Regards", "Per my last email"],
counter: 0,
};
const matrix = [
["Hi there!", "Sup dude", "Salutations"],
["Regards", "Hasta luego", "Byebye"],
[
"Per my last email",
"oopsie!",
"some other option here, you're writing this game",
],
];
const wordZone = $("#wordZone");
const choiceButtons = $("#choices li");
$("#choices").hide();
$("#end").hide();
$("#startBtn").click(function () {
$("#choices").show();
});
function buildOptions(wordCounter) {
let turnChoices = matrix[wordCounter];
$("#next").hide();
for (let i = 0, ii = turnChoices.length; i < ii; i++) {
let choiceWord = turnChoices[i];
let choiceButton = $(choiceButtons[i]);
let btnClass = "incorrect";
choiceButton.text(choiceWord);
if (dict.officeSpeak.indexOf(choiceWord) != -1) {
btnClass = "correct";
}
choiceButton.addClass(btnClass);
}
}
buildOptions(matrixcounter);
function onClickWord(e) {
console.log($(this));
$("#choices li").addClass("active");
$(".correct").css("color", "green");
$(".incorrect").css("color", "red");
if ($(this).hasClass("correct")) {
score++;
console.log(score);
}
$("#next").show();
let turnChoices = matrix[+1];
}
$("#choices li").click(onClickWord);
$("#next").click(function () {
matrixcounter++;
buildOptions(matrixcounter);
$(".correct").css("color", "black");
$(".incorrect").css("color", "black");
});
function finalScore() {
if (matrixcounter >= buildOptions(turnChoices.length)) {
$("#end").show();
}
$("#end").click(function () {
return score;
});
// let finalScore = score;
// $("#wordZone").show(finalScore);
// if (finalScore >= 2) {
// $("#wordZone").addClass("win");
// $("#win").show();
// } else {
// $("#wordZone").addClass("lose");
// $("#lose").show();
// }
}
finalScore();
I tried setting up questions as such:
const questions = [{ question: "Did you get the email I sent 5 minutes ago? Havent heard from u.", choices: ["I'm busy", "You just sent it", "Havent had a chance to look!",], correctAnswer: "Havent had a chance to look!" }, {
however it broke the loop I had previously set up to have matrix read dict as an answer key.

Angular - inconsistent countdown with angular material progressbar

I have a countdown timer:
interval;
timeJump: number = 10;
timeLength: number = 5;
timerLeft: number = this.timeLength;
startTimer() {
this.interval = setInterval(() => {
if (this.timerLeft - this.timeJump / 1000 <= 0) {
this.timerLeft = 0;
clearInterval(this.interval);
return;
setTimeout(() => this.finishedTimer(), 500);
} else {
this.timerLeft -= this.timeJump / 1000;
}
}, this.timeJump);
}
I am calling the startTimer() function in ngOnInit(), and it is running.
In the html template I have a progress bar:
<div class="progress-wrapper" style="height: 4%;">
<mat-progress-bar id="timer" mode="determinate" value="{{(timerLeft/timeLength) * 100}}"
style="height: 100%;"></mat-progress-bar>
</div>
When the timer reaches the end, about the last 10%, it goes down in value a lot quicker than it should.
Can you help me fix this?
Thanks
Is this a problem? Work perfect for me!
(except for setTimeout(() => this.finishedTimer(), 500); after return; never be used)
But, you can use RxJs for that!
HTML
<mat-progress-bar mode="determinate" [value]="progressbarValue"></mat-progress-bar>
TS
import { interval } from 'rxjs';
...
progressbarValue = 100;
curSec: number = 0;
startTimer(seconds: number) {
const time = seconds;
const timer$ = interval(1000);
const sub = timer$.subscribe((sec) => {
this.progressbarValue = 100 - sec * 100 / seconds;
this.curSec = sec;
if (this.curSec === seconds) {
sub.unsubscribe();
}
});
}
And call startTime method with progress bar timeout.
startTimer(120) // 120 seconds
References: https://stackblitz.com/edit/angular-material-progress-bar-decrease
I fixed it!
The problem was I was using too short of an interval. The progress bar couldn't keep up with the speed. Set the interval to whatever you want, but always round the value you put in the value of the progress bar to a whole number, like this:
timeLeft | number : '1.0-0'

How to implement duration picker with HTML5 or/with Angular8, with hours more than 24?

I am trying to implement a control, using either
<input type="time"/>
or just with
<input type="text"/>
and implement a duration picker control which can have hours format more than 24, something like 000:00:00 or hhh:mm:ss, and no am/pm option ( The default input type for time has formats in am/pm format, which is not useful in my case).
The requirement is to be able to increase decrease the duration using up and down keys much like the default input type time of HTML.
Is there any native HTML, angular, or material component for this?
Or is there a way to achieve this using regular expression/patterns or something?
One way I can think of is to write your custom control (as also mentioned by #Allabakash). For Native HTML, The control can be something like this:
window.addEventListener('DOMContentLoaded', (event) => {
document.querySelectorAll('[my-duration-picker]').forEach(picker => {
//prevent unsupported keys
const acceptedKeys = ['Backspace', 'ArrowLeft', 'ArrowRight', 'ArrowDown', 'ArrowUp'];
const selectFocus = event => {
//get cursor position and select nearest block;
const cursorPosition = event.target.selectionStart;
"000:00:00" //this is the format used to determine cursor location
const hourMarker = event.target.value.indexOf(":");
const minuteMarker = event.target.value.lastIndexOf(":");
if (hourMarker < 0 || minuteMarker < 0) {
//something wrong with the format. just return;
return;
}
if (cursorPosition < hourMarker) {
event.target.selectionStart = 0; //hours mode
event.target.selectionEnd = hourMarker;
}
if (cursorPosition > hourMarker && cursorPosition < minuteMarker) {
event.target.selectionStart = hourMarker + 1; //minutes mode
event.target.selectionEnd = minuteMarker;
}
if (cursorPosition > minuteMarker) {
event.target.selectionStart = minuteMarker + 1; //seconds mode
event.target.selectionEnd = minuteMarker + 3;
}
}
const insertFormatted = (inputBox, secondsValue) => {
let hours = Math.floor(secondsValue / 3600);
secondsValue %= 3600;
let minutes = Math.floor(secondsValue / 60);
let seconds = secondsValue % 60;
minutes = String(minutes).padStart(2, "0");
hours = String(hours).padStart(3, "0");
seconds = String(seconds).padStart(2, "0");
inputBox.value = hours + ":" + minutes + ":" + seconds;
}
const increaseValue = inputBox => {
const rawValue = inputBox.value;
sectioned = rawValue.split(':');
let secondsValue = 0
if (sectioned.length === 3) {
secondsValue = Number(sectioned[2]) + Number(sectioned[1] * 60) + Number(sectioned[0] * 60 * 60);
}
secondsValue += 1;
insertFormatted(inputBox, secondsValue);
}
const decreaseValue = inputBox => {
const rawValue = inputBox.value;
sectioned = rawValue.split(':');
let secondsValue = 0
if (sectioned.length === 3) {
secondsValue = Number(sectioned[2]) + Number(sectioned[1] * 60) + Number(sectioned[0] * 60 * 60);
}
secondsValue -= 1;
if (secondsValue < 0) {
secondsValue = 0;
}
insertFormatted(inputBox, secondsValue);
}
const validateInput = event => {
sectioned = event.target.value.split(':');
if (sectioned.length !== 3) {
event.target.value = "000:00:00"; //fallback to default
return;
}
if (isNaN(sectioned[0])) {
sectioned[0] = "000";
}
if (isNaN(sectioned[1]) || sectioned[1] < 0) {
sectioned[1] = "00";
}
if (sectioned[1] > 59 || sectioned[1].length > 2) {
sectioned[1] = "59";
}
if (isNaN(sectioned[2]) || sectioned[2] < 0) {
sectioned[2] = "00";
}
if (sectioned[2] > 59 || sectioned[2].length > 2) {
sectioned[2] = "59";
}
event.target.value = sectioned.join(":");
}
const controlsDiv = document.createElement("div");
const scrollUpBtn = document.createElement("button");
const scrollDownBtn = document.createElement("button");
scrollDownBtn.textContent = " - ";
scrollUpBtn.textContent = " + ";
scrollUpBtn.addEventListener('click', (e) => {
increaseValue(picker);
});
scrollDownBtn.addEventListener('click', (e) => {
decreaseValue(picker);
});
picker.parentNode.insertBefore(scrollDownBtn, picker.nextSibling);
picker.parentNode.insertBefore(scrollUpBtn, picker.nextSibling);
picker.value = "000:00:00";
picker.style.textAlign = "right"; //align the values to the right (optional)
picker.addEventListener('keydown', event => {
//use arrow keys to increase value;
if (event.key == 'ArrowDown' || event.key == 'ArrowUp') {
if(event.key == 'ArrowDown'){
decreaseValue(event.target);
}
if(event.key == 'ArrowUp'){
increaseValue(event.target);
}
event.preventDefault(); //prevent default
}
if (isNaN(event.key) && !acceptedKeys.includes(event.key)) {
event.preventDefault(); //prevent default
return false;
}
});
picker.addEventListener('focus', selectFocus); //selects a block of hours, minutes etc
picker.addEventListener('click', selectFocus); //selects a block of hours, minutes etc
picker.addEventListener('change', validateInput);
picker.addEventListener('blur', validateInput);
picker.addEventListener('keyup', validateInput);
});
});
<input type="text" my-duration-picker></input>
Tested and working on Google Chrome 78. I will do a Angular version later.
For the Angular version, you can write your own custom Directive and just import it to your app-module-ts declarations. See this example on stackblitz:
App Demo: https://angular-xbkeoc.stackblitz.io
Code: https://stackblitz.com/edit/angular-xbkeoc
UPDATE: I developed and improved this concept over time. You can checkout the picker here 👉 https://nadchif.github.io/html-duration-picker.js/
checkout this solution , https://github.com/FrancescoBorzi/ngx-duration-picker. which provides options you are looking for.
here is the demo - https://embed.plnkr.co/1dAIGrGqbcfrNVqs4WwW/.
Demo shows Y:M:W:D:H:M:S format. you can hide the parameters using flags defined in docs.
Since you are looking for duration picker with single input, creating your own component will be handy.
You can consider the concepts formatters and parsers.
checkout this topics which helps you in achieving that.
https://netbasal.com/angular-formatters-and-parsers-8388e2599a0e
https://stackoverflow.com/questions/39457941/parsers-and-formatters-in-angular2
here is the updated sample demo - https://stackblitz.com/edit/hello-angular-6-yuvffz
you can implement the increase/decrease functionalities using keyup/keydown event functions.
handle(event) {
let value = event.target.value; //hhh:mm:ss
if(event.key === 'ArrowUp') {
console.log('increase');
} else if (event.key === 'ArrowDown') {
console.log('decrease');
} else {
//dont allow user from entering more than two digits in seconds
}
}
Validations you need to consider ::
- If user enters wrong input, show error message / block from entering anything other than numbers
- allowing only unit specific digits - (Ex :: for hr - 3 digits, mm - 2 digits etc as per your requirement)
To do something more interesting or make it look like interactive you can use the
flipclock.js which is very cool in looking and to work with it is also feasible.
Here is the link :-
http://flipclockjs.com/
You can try with number as type :
<input type="min" min="0" max="60">
demo :
https://stackblitz.com/edit/angular-nz9hrn

HTML - Time Input with up to 100 minutes

Is there any way to manipulate the html time input, so I can add up to 100 Minutes and 59 seconds?
No. Time input is for time. Your clock is more than 60 minutes?
Another way is to write custom input field with JavaScript. Or add number input field and then with JavaScript calculate hours/minutes/seconds ...
Edit: After re-reading the original post, I'm pretty sure I'm answering the wrong question here. But I'll leave it in case someone else finds it useful.
If you're referring to <input type="time">, you can access and modify the value like a normal input element:
var time = document.getElementById('time');
var button = document.getElementById('button');
function handleButtonClick() {
var parts = time.value.split(':');
var hour = parseInt(parts[0]);
var minute = parseInt(parts[1]);
hour += 1;
minute += 7;
// TODO: Need to validate the new time
time.value = hour + ':' + minute;
}
button.addEventListener('click', handleButtonClick, false);
<input id="time" type="time">
<button id="button">Process</button>
You can use Jquery for this:
function addMinutes(time, minsToAdd) {
function D(J){ return (J<10? '0':'') + J;};
var piece = time.split(':');
var mins = piece[0]*60 + +piece[1] + +minsToAdd;
return D(mins%(24*60)/60 | 0) + ':' + D(mins%60);
}
$("#add").click(function(){
var time = $("#time").val();
var new_time = addMinutes(time, '100');
alert(new_time);
});
Working DEMO