0

Still trying to find an answer to Aurelia JS - Making a synchronous HTTP request, to change data before page load? - so I've reduced it to the following question.

Let's say we work with contact manager tutorial:

... whose code is also copied on https://gist.run/?id=c73b047c8184c052b4c61c69febb33d8 ...

Now, this is how I read the code: in contact-list.js, in the constructor() of ContactList class, we have:

export class ContactList {
  static inject = [WebAPI, EventAggregator];

  constructor(api, ea){
    this.api = api;
    this.contacts = [];
...

... so in the constructor, this.contacts of the ContactList class is initialized to an empty array.

Then, in the same ContactList class, there is a created() method:

created(){
  this.api.getContactList().then(contacts => this.contacts = contacts);
}

This retrieves the contact list as defined in web-api.js, and it assigns it to the class ContactList property this.contacts, which was previously empty.

Finally, in contact-list.html, we have:

  <li repeat.for="contact of contacts" class="list-group-item ${contact.id === $parent.selectedId ? 'active' : ''}">
  ...

... which apparently iterates through this.contacts of the class ContactList, and makes <li> (and other) elements in the HTML GUI based on that.

So, as I understand this, the idea is that whenever the this.contacts property of the ContactList class changed, then the <li repeat.for="contact of contacts" ... should execute again, and show an updated GUI in accordance with the data.

However, I cannot get to demonstrate this. The easiest, I thought, would be to have a function execute a few seconds after the created() method of ContactList has run, so I tried to use setTimeout for that:

created(){
  this.api.getContactList().then(contacts => this.contacts = contacts);
  setTimeout(this.changeContactList, 3000); // call changeContactList() function after 3 seconds
}

... and I've added a changeContactList method which is this:

  changeContactList() {
    this.contacts = [  {
      id:13,
      firstName:'Bob',
      lastName:'Rock',
      email:'bob@rock.com',
      phoneNumber:'888-7303'
    }
    ];
    alert("changeContactList done " + this.contacts.length);
  }

So, it is just a simple assignment of this.contacts of the ContactList class to a new data array.

So, for this, I do indeed get an alert window after some seconds; it does say "changeContactList done 1" meaning that the this.contacts array has indeed been changed to the new data - except, there are no changes whatsoever in the GUI?!

So what am I doing wrong? Am I supposed to call an additional function to have the updated state render? But if I have to call an additional function, what is the point of binding, then? In other words - what do I have to do, to get the GUI to update and show the newly changed state of the this.contacts array?

Community
  • 1
  • 1
sdbbs
  • 4,270
  • 5
  • 32
  • 87

1 Answers1

0

Ok, found what the problem is - the problem with the code above is that the meaning of this changes when you use setTimeout - in that case, this becomes a reference to Window, not to an instance of the defining class! (the rest of the binding apparently works as I had understood it previously)

With that in mind, I finally got the GUI to show the updated data array with the following changes in contact-list.js:

  created(){
    this.api.getContactList().then(contacts => { this.contacts = contacts ;
      //setTimeout(this.changeContactList, 1000); // timeout delay ok, but 'this' becomes Window
      //setTimeout(this.changeContactList(this), 1000); // timeout delay not honored here
      //setTimeout(function() {console.log(this); this.changeContactList(this);}, 1000); // "this.changeContactList is not a function"; 'this' is Window

      // works OK:
      //var copythis = this; // make a copy of 'this', since 'this' looses meaning in the setTimeout
      //setTimeout(function() {console.log(copythis); copythis.changeContactList();}, 1000); // 
    });
    console.log(this.contacts); // is empty [] here - Promise unresolved yet

    // also works OK:
    var copythis = this; // make a copy of 'this', since 'this' looses meaning in the setTimeout
    setTimeout(function() {console.log(copythis); copythis.changeContactList();}, 2000); // 
  }

  changeContactList() {
    //this.contacts = [  {
    //  id:13,
    //  firstName:'Bob',
    //  lastName:'Rock',
    //  email:'bob@rock.com',
    //  phoneNumber:'888-7303'
    //}
    //];
    // "this" is Window here, if called from setTimeout(this.changeContactList,
    // but if called as setTimeout(this.changeContactList(this), then "this" is ContactList! - but then timeout delay is not honoured

    console.log(this);
    console.log(this.contacts);
    this.contacts.push({
      id:13,
      firstName:'Bob',
      lastName:'Rock',
      email:'bob@rock.com',
      phoneNumber:'888-7303'
    });
    alert("changeContactList done " + this.contacts.length);
  }
sdbbs
  • 4,270
  • 5
  • 32
  • 87