What is the best approach to incorporate a stopwatch?

I'm exploring ways to track the time it takes for a user to click a button. While I have a working solution, I'm curious if there's a more efficient method available. Below is my current implementation:

export class MainComponent implements OnInit {

    timer : number = 0;
    intervalId : number;

    constructor() {
      this.intervalId = setInterval(() => {
        this.timer++;
      }, 1000);
    }

    ngOnInit() {}

    buttonClick = function() {
    alert(this.timer);
    this.timer = 0;
    }
}

Answer №1

Opt for performance.now() to ensure precise time-stamps (or fallback to new Date().getTime()) and calculate the time difference in UI update callbacks using setInterval. Avoid relying on setInterval to track time as it may not execute exactly every 1000ms.

Additionally, I have transferred the timer logic to the ngOnInit method instead of keeping it in the constructor function.

export class MainComponent implements OnInit {

    private start: number = null;
    private uiTimerId: number = null;

    constructor() {
    }

    private updateUI(): void {

        let delta = performance.now() - this.start;
        this.someUIElement.textContent = delta.toFixed() + "ms";
    }

    ngOnInit() {

        this.start = parseFloat( window.localStorage.getItem( "timerStart" ) );
        if( !this.start ) {
            this.start = performance.now();
            window.localStorage.setItem( "timerStart", this.start );
        }

        this.uiTimerId = window.setInterval( this.updateUI.bind(this), 100 ); // Update UI every 100ms for smoother interface
    }

    buttonClick = function() {
        if( this.uiTimerId != null ) {
            window.clearInterval( this.uiTimerId );
            window.localStorage.removeItem( "timerStart" );
        }
    }
}

Answer №2

First and foremost, it's important to note that we define our member functions differently in TypeScript. Therefore, the buttonClick function should be written as follows:

buttonClick() {
  alert(this.timer);
  this.timer = 0;
}

As suggested by @Dai, a more efficient and accurate approach would be to get the system time at initialization (ngOnInit) and calculate the difference from the system time on click.

ngOnInit() {
  this.startTime = localStorage.startTime ? JSON.parse(localStorage.startTime) : (new Date().getTime());
  localStorage.setItem('startTime', JSON.stringify(this.startTime));
}

buttonClick() {
  this.startTime = JSON.parse(localStorage.startTime);
  alert((this.startTime - (new Date().getTime())) / 1000);
}

UPDATE: I have revised the answer to demonstrate how to utilize localStorage for persisting values. While similar to the previous solution, this approach employs idiomatic TypeScript practices. For those accustomed to ES5 methods, there is no issue with using them; however, I find this style to be clearer and easier to understand. To enhance your TypeScript skills, consider going through an Angular tutorial such as the "Tour of Heroes" on the official website. Additionally, using Visual Studio Code with the Angular Essentials plugin can help format and lint your code correctly. This will assist you in familiarizing yourself with idiomatic TypeScript coding. Best of luck!

Answer №3

Here is an alternative way to code a stopwatch without using an increment timer. The timer's increment will adjust when the tab is inactive. I have implemented a stopwatch in a highly efficient manner that allows for seamless switching between tabs without affecting the time count. You can easily reset, pause, and play the stopwatch. For more details, you can refer to the provided link.

  seconds: string = "00";
  minutes: string = "00";
  hours: string = "00";
   timer(){
    let mcountercal = 0;
    let currentSeconds = parseInt(this.seconds);
    let currentMinutes = parseInt(this.minutes);
    let currentHours = parseInt(this.hours);
    this.counter = currentHours * 3600000 + currentMinutes * 60000 + currentSeconds * 1000
    const startTime = Date.now() - (this.counter || 0);
    this.timeoutId = setInterval(() => {
      this.counter = Date.now() - startTime;
      currentHours = Math.floor(this.counter / 3600000);
      currentMinutes = Math.floor(this.counter / 60000) - currentHours * 60;
      mcountercal = Math.floor(this.counter / 60000);
      currentSeconds = Math.floor(this.counter / 1000) - mcountercal * 60;     
      this.hours = this.getFormattedTimeStamp(currentHours.toString());
      this.minutes = this.getFormattedTimeStamp(currentMinutes.toString())
      this.seconds = this.getFormattedTimeStamp(currentSeconds.toString())
}

getFormattedTimeStamp(timestamp:any) {
  return timestamp < 10 ? "0" + timestamp : timestamp;
}

https://stackblitz.com/github/Ashraf111/StopWatchWhenTabActiveAndInactive

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Generating interactive forms in Angular 7 using API data

I have developed a component that generates dynamic forms using angular reactive forms. When I attempt to utilize this component to create form fields in another component based on the response from my API, the form appears disabled and I am unable to make ...

How to Use Hyperledger Composer's Rest-Server-Api Local-Passport Strategy in an Angular Application without Node.js

Is it possible for me to implement the passport-local strategy in my project, considering that I am using an angular front-end generated by the composer-rest-server tool? I noticed in the documentation for passportjs regarding passport-local, it mentions ...

Develop an extensive Typescript and React shared library

Trying to develop a shared React and Typescript library has been quite challenging. Configuring the project workspace to work on both the library and application simultaneously has proven to be more difficult than anticipated. project ├─ app │ ├ ...

What is the best way to convert one array of types to another array of types in Typescript?

Imagine you have the following: type AwesomeTuple = [string, number, boolean] Now, you're looking to transform that type using a generic approach: type AmazingGeneric<T extends any[]> = ... In this scenario: AmazingGeneric<AwesomeType> w ...

Why is @faker-js/faker not usable in a TypeScript project, showing undefined error, while the older "faker" import still functions correctly?

Currently, my packages.json file includes: "faker": "^5.5.3", "@types/faker": "^5.5.3", I am sticking with version 5.5.3 due to another project dependency (codecept) that requires this specific version. The ...

Updates made to Angular components do not get transpiled to JavaScript

Embarking on my first ASP.NET Core application journey with Angular 2! User access is a top priority for the application. Facing the absence of an Angular template in Visual Studio 2017, I opted to use Powershell and Yoman to generate an Angular project s ...

The NgModel variable, which is exported through Angular7 template driven validation, appears to be returning an

Trying to set up a straightforward template-driven form validation, I encountered an issue with #password="ngModel". When I check password.length, it returns undefined and I'm not sure why. Here is my Angular form: <form #f="ngForm"> <inp ...

Angular service encountering duplicated arrays when retrieving data from Firebase using HTTP.get

While working on my Angular web application, I encountered a strange issue with duplicate arrays in the response from an HTTP get call to a Firebase realtime database. The Firebase setup includes a realtime database configured like this: Firebase data I ...

Using the expect statement within a Protractor if-else block

My script involves an if-else condition to compare expected and actual values. If they do not match, it should go to the else block and print "StepFailed". However, it always executes the if block and the output is "step passed" even when expected does not ...

Error: Angular encountered an undefined variable when attempting to import 'node_modules/bootstrap/scss/grid'

Having some trouble setting up Angular with SCSS and Bootstrap. When attempting to run ng serve, I encountered the following error: ./src/styles.scss - Error: Module build failed (from ./node_modules/sass-loader/dist/cjs.js): SassError: Undefined variable. ...

The State of NgRX Entity is encountering undefined IDs

I decided to experiment with @ngrx/entity in a simple "Todo" project, where I had only one AppModule, one reducer, and a single component. However, as I delved into it, I encountered some challenges. The actions I defined were quite basic, focusing on CRU ...

Converting European date format to a standard format

THE ISSUE: The dates retrieved from the API are in European format: 01-06-2018 This means day - month - year (1 June 2018). I want to display these dates in a more visually appealing way - the 'd MMM y' format seems perfect for this purpose ...

Troubleshooting Angular 2 routes failing to function post aot compilation deployment

Currently, I am implementing RouterModule in my project and have the following configuration in my app.module.ts file: const appRoutes: Routes = [ { path: '', redirectTo: 'mainMenu', pathMatch: 'full' }, { path: 'mainMen ...

How to use TypeScript to filter an array based on the values of another array

Suppose I have two arrays. The first one looks like this: names: [{ value: 'recordedData', desc: 'Data' } { value: 'recordedNumbers', desc: 'numbers' } { value: 'recordedNames', desc: 'name ...

Angular Service singleton constructor being invoked multiple times

I have been facing an issue with using an app-wide service called UserService to store authenticated user details. The problem is that UserService is being instantiated per route rather than shared across routes. To address this, I decided to create a Core ...

When navigating using the next and back buttons, the active state in Angular is automatically removed

Looking for some assistance with my quiz app setup. Each question has True/False statements with corresponding buttons to select T or F. However, when I click the next/back button, the active class is not being removed from the previous selection. As a beg ...

Steps to prevent subfolder imports in my npm package

My npm package is built using: typescript webpack webpack.config: {... entry: './src/index.ts } library tree: - package.json - src - - index.ts - - ...all_my_code... I have all my library functionality and types exported from the index.ts file. T ...

The file isn't located in 'rootDir', even though all the details seem to be accurate

I'm currently troubleshooting an issue with one package in my nx monorepo that is causing the error code "TS6059". Interestingly, all other packages are functioning correctly in previous builds. After reviewing my index.ts files, it appears that all ...

Looking for a TypeScript annotation that allows accessing an object property using a variable

When working with plain JavaScript, we have the ability to access an object's property value using a variable. For example, this is permitted: let obj = { a: 100, b: 'Need help with TypeScript', c: new Date() }; let prop = 'b'; c ...

I need to confirm the permissions of the token that I receive from AAD when using microsoft-ada-angular6 and microsoft-graph-client

Currently, I am implementing the Implicit Grant workflow in Angular utilizing the microsoft-adal-angular6 library to authenticate users within my application and then acquire a token for accessing the Microsoft Graph. Fortunately, the authentication proce ...