0

I am trying to iterate over an array of objects to mutate a property. I am using Angular and when I run the ngOnChanges for the first time the functions runs but on the second iteration ( 60 seconds later) the array length is showing as undefined and reporting "Cannot read property length of undefined" even though it was literally defined a minute ago. Can someone please explain wtf is happening here

Code follows

ngOnChanges():void
  {
    console.log(this.dealership)
    this.service.getActiveOffers(this.dealership.dealerid)
    .subscribe((response) =>
    { 
      console.log(response)
      this.activeQuotes = response;
      if(this.activeQuotes.length > 0)
      {
        this.checkTime()
      }            
      setInterval(this.checkTime,60000)
      
    }, (err) =>
    {
      console.log(err)
    })
  }



  getExpHours(expiryDate)
  {
    let now =  new Date();
    let exp = new Date(expiryDate)
    var timeDiff = (exp.getTime() - now.getTime()) / 1000;
    timeDiff /= 60;
    let hours = timeDiff/ 60;
    return hours.toFixed(0)    
  }
  getExpMin(expiryDate)
  {
    let now =  new Date();
    let exp = new Date(expiryDate)
    var timeDiff = (exp.getTime() - now.getTime()) / 1000;
    timeDiff /= 60;
    let mins = timeDiff/ 60 / 60;
    return mins.toFixed(2).split(".")[1]
  }
  checkTime()
  {
    console.log(this.activeQuotes.length)
    for(let i = 0; i < this.activeQuotes.length; i++)   
      {
        this.activeQuotes[i].expHours = this.getExpHours(this.activeQuotes[i].expiry)
        this.activeQuotes[i].expMins = this.getExpMin(this.activeQuotes[i].expiry)
      }
      console.log(this.activeQuotes)
  }

Value of Array Objects

Array(2)
0:
accepted: false
color: "white"
dealerid: 1
expHours: "21"
expMins: "35"
expiry: "2020-06-24T19:12:48.152Z"
inqid: 2
make: "honda"
model: "accord"
offerid: 2
prodyear: 2020
quoteid: 2
totalvalue: "$19,000.00"
trimtype: "LX"
vehicleid: 31

1:
accepted: false
color: "blue"
dealerid: 1
expHours: "24"
expMins: "39"
expiry: "2020-06-24T22:03:57.210Z"
inqid: 1
make: "honda"
model: "civic"
offerid: 3
prodyear: 2019
quoteid: 3
totalvalue: "$2,000.00"
trimtype: "LX"
vehicleid: 26

Thank you.

d0rf47
  • 409
  • 8
  • 20
  • Either use arrow function notation (`setInterval(() => { this.checkTime() },60000)`) or [`bind`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind) function (`setInterval(this.checkTime.bind(this),60000)`) to specify that the value of `this` keyword denotes the scope of the class. – ruth Jun 23 '20 at 22:35

1 Answers1

2

You lose the correct this context by passing checkTime to setInterval. You can bind it again using bind like this

setInterval(this.checkTime.bind(this),60000)
Đinh Carabus
  • 3,403
  • 4
  • 22
  • 44
  • WOW I cannot believe that. I fixed it and i will accept your answer in 8 mins lol when stack lets me. But can you elaborate on something. In the function checkTime(), why exactly do I need to bind this, when in the function I use the this.activeQuotes, shouldn't it always use the current state of the array? Thanks btw – d0rf47 Jun 23 '20 at 22:36
  • When you call a function (or rather "method" in this case) that is attached to an object like this `obj.method()` the interpreter automatically sets the `this` context to be the `obj`. In your case you do not call but pass your method by itself to setInterval. Because setInterval does not know about the original context it calls the method after the timeout using the default `this` context which is the global object (window). – Đinh Carabus Jun 23 '20 at 22:42
  • 1
    thanks mate. I learned about that in React But I should've known it would apply here too :\ – d0rf47 Jun 23 '20 at 23:36