How can parameters be accessed outside the $timeout callback in Angular?

My code includes a timeout loop structured like this:

var somedata = {
        autoRefreshInterval: 300,
        autoRefreshInSec: 300,
        myTimeout: null,
        doRefresh: _doRefresh,
        onTimeout: function () {
            this.autoRefreshInSec--;
            if (this.autoRefreshInSec <= 0) {
                this.autoRefreshInSec = this.autoRefreshInterval; 
                this.doRefresh();
            }
            this.myTimeout = $timeout(this.onTimeout, 1000);
        },
        startTimer: function () {
            this.autoRefreshInSec = this.autoRefreshInterval;
            this.myTimeout = $timeout(this.onTimeout, 1000);
        },
        stopTimer: function () {
            $timeout.cancel(this.myTimeout);
        },
    }

It seems that the use of "this" within the onTimeout callback function is causing issues, whereas it functions correctly in startTimer and stopTimer. How can I resolve this?

UPDATE:

Because the context of "this" is lost inside onTimeout as explained in one of the answers below, I attempted to pass it in like this:

onTimeout: function (self) {
    self.autoRefreshInSec--;
    if (self.autoRefreshInSec <= 0) {
        self.autoRefreshInSec = self.autoRefreshInterval;  
        self.doRefresh();
    }
    self.myTimeout = $timeout(self.onTimeout(self), 1000);
},
startTimer: function () {
    this.autoRefreshInSec = this.autoRefreshInterval;
    this.myTimeout = $timeout(this.onTimeout(this), 1000);
},

Interestingly, while debugging the code appeared to work. However, upon removing breakpoints, self.doRefresh() starts executing continuously. Why is this happening?

UPDATE 2:

To demonstrate the issue, I have created a JSFiddle at http://jsfiddle.net/qY86q/1.

Answer №1

Function.prototype.bind()

The bind() function is used to create a new function where the value of 'this' keyword is set to a specified value, along with any additional arguments that are passed when the new function is called.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Solution tailored for your scenario

angular.module('myApp', [])
  .service('timerService', function($timeout) {
    var _timer = {
      autoRefreshInterval: 300,
      autoRefreshInSec: 300,
      myTimeout: null,
      onTimeout: function() {
        this.autoRefreshInSec -= 1;
        if (this.autoRefreshInSec <= 0) {
          this.autoRefreshInSec = this.autoRefreshInterval;
          console.log('refreshing');
        }
        console.log('time: ', this.autoRefreshInSec);
        this.myTimeout = $timeout(this.onTimeout.bind(this), 1000);
      },
      startTimer: function() {
        if (this.myTimeout) {
          this.stopTimer(this.myTimeout)
        }
        this.autoRefreshInSec = this.autoRefreshInterval;
        this.myTimeout = $timeout(this.onTimeout.bind(this), 1000);
      },
      stopTimer: $timeout.cancel // see note(1)
    };
    var context = {
      timer: _timer
    };
    return context;
  }).controller('PrefsCtrl', function PrefsCtrl($scope, timerService) {
    $scope.timer = timerService.timer;
  })
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="myApp">
  <div ng-controller="PrefsCtrl">
    <button ng-click="timer.startTimer()">Click to Start or Reset Timer</button>
    <div>{{timer.autoRefreshInSec}}</div>
  </div>
</div>

note(1), this is shorcut for

stopTimer: function(timer) {
   $timeout.cancel(timer)
}

Answer №2

There seems to be a slight complication with the javascript binding in this case.

Here is a possible solution:

var customData;  // now we can reference this in the angular.bind function
customData = {
    onEvent: angular.bind(customData, function () {
        this.count--;
        if (this.count <= 0) {
            this.count = this.interval;
            this.handleEvent();
        }
        this.timer = $timeout(this.onEvent, 1000);
    })
}

This method will ensure that the context of this inside the callback function is specifically tied to customData.

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

Retrieve a document from a server and specifying the file name using AngularJS

Attempting to download a zip file from my server using a Spring MVC controller. Here is the AngularJS (1.5) controller code I am using to achieve this: $http({ url: '/myurl', method: 'GET', headers: { 'Content- ...

Error in React Router when using TypeScript

Encountering errors while trying to set up router with React and TypeScript. https://i.sstatic.net/muSZU.png I have already attempted to install npm install @types/history However, the issue persists. Your assistance would be greatly appreciated. Thank y ...

Dynamically loading an AngularJS controller

I am faced with the challenge of integrating an Angular app with dynamically loaded controllers into an existing webpage. Below is a code snippet where I have attempted to achieve this based on my understanding of the API and some research: // Create mod ...

Exploring how Ruby interacts with AngularJS variables in a view

Currently, I have implemented angular's ng-repeat in a Ruby .erb view. I now need to create a link_to a page that is dedicated to a specific IP address. Initially, I attempted: <td><%= link_to '{{ roll.system }}' ,'server/&apo ...

Adding text in CKEditor with Angular while preserving the existing formatting

To add my merge field text at the current selection, I use this code: editor.model.change(writer => { var position = editor.model.document.selection.getFirstPosition(); // trying to connect with the last node position.stickiness = 'toP ...

What is the process for creating a multi-word argument?

Is there a way to create multi-word arguments, such as reasons for bans or mutes? Currently, I am using args[2], args[3], args[4], args[5]... but this approach is limited and if nothing is written in those arguments, it will display "undefined". If you k ...

Retrieve the current item within an ng-repeat loop

Here's a scenario I'm dealing with: <li ng-repeat="item in items"> {{item.name}} </li> The item object has an additional property called index. How can I assign this to the tabIndex so that my output appears as follows: <li ...

Understanding the optimal timing for when the user interface is fully functional in React/Redux

I am seeking to determine the time it takes for my app to be fully "ready" for user interaction. The app loading process includes the following steps: DOM load. Initial React render. HTTP calls triggered from various componentDidMount()s HTTP calls retur ...

Tips for updating the value of a key in a JavaScript object when the TextField tag's value changes

Here is the schema I am using for my model: const workoutSchema = mongoose.Schema({ workouts: [ { workoutName: String, sets: Number, reps: Number, weight: Number, }, ], }); Below is the postData referenced in the text f ...

Use JavaScript to limit Google Analytics and Yandex.Metrica to track only the referral sources and screen sizes

I prefer not to include external JavaScript on my website for unnecessary tracking purposes. However, I do need to gather referrer and screen size information, which cannot be achieved with a regular HTML img tag alone. What is the most standard-complian ...

Error alert: Object expected on OnClientClick in Microsoft JScript runtime

I was in the middle of a quick test when I encountered an error. At this point, I haven't implemented any C# code yet and my aspx code looks like this: <script language=javascript type="text/javascript"> function myOnClick() { ...

Several middlewares using router.params()

Is it possible to include multiple middlewares as parameters in the function router.params() in Node-Express? I currently have the following setup: const checkAuth = (req, res, next) => {console.log("checking auth"); next()} const checkAuth = ...

PhoneGap 3.5.0 FileTransfer onprogress issue unresolved

I can't seem to get the onprogress event handler to work when downloading a file. The success callback is triggered and the download goes through successfully, but for some reason, the progress events are not firing. Does anyone see any issues with my ...

Transfer data via ajax to the controller

I need assistance with storing a file in my database using Tabulator without having a form already created. I am currently creating a simple input element like this: var editor = document.createElement("input");. After clicking the file, it trigg ...

3D Carousel with Dynamic CSS, Perfect for Z-Axis Positioning

I am currently working on creating a 3D carousel using CSS and javascript. To view what I have accomplished so far, you can visit this page: Upon arriving at the page, please click the "initialize" button to transform the carousel into 3D space. By defaul ...

In JavaScript, the event.defaultPrevented property will never be set to true

I've been experimenting with events in my script, but I'm struggling to understand the functionality of event.preventDefault()/event.defaultPrevented: var e = new Event('test'); e.defaultPrevented; > false e.preventDefault(); e.d ...

The workings of the toString() function within Objects

Recently while delving into a book on Js, I stumbled upon the intriguing topic of Object to primitive conversion. The author made an interesting point in the book: For historical reasons, if toString or valueOf returns an object, there’s no error, but ...

Trouble with Mongoose version 1.0.2 on repl.it - connection not established

Currently, I am facing an issue with hosting a discord bot on repl.it as it is not connecting to MongoDB on my laptop. Despite creating an account, I am unsure how to establish the connection. I have been attempting to resolve this for nearly two weeks n ...

The jQuery live data appears to be unresponsive when interacting with the DOM

Currently, I am utilizing a jQuery plugin called tokeninput which facilitates autosuggestion while typing: Below is the code responsible for generating autosuggestions for the input box: $("#input-auto").tokenInput("topicGet.php", { theme ...

The shadow feature in Three.js doesn't seem to be functioning properly

I'm having trouble getting the Three.js shadow effect to work in version r82. I believe I have everything set up correctly, but for some reason it's not working. Can anyone point out what I might be missing? Here is an example link for referen ...