skip to Main Content

I have this custom HTML element causing me trouble. Down in the StaffList connectedCallback() function, i have to call and outside function of the parrent but the super keyword for addStaff() is giving me an error. "Uncaught SyntaxError: use of super property accesses only valid within methods or eval code within methods". How can i access something outside the request?

class StaffList extends HTMLElement {

  // A getter/setter for a disabled property.
  get selected() {
    return this.hasAttribute('selected');
  }
  set selected(val) {
    // Reflect the value of the disabled property as an HTML attribute.
    if (val) {
      this.setAttribute('selected', '');
    } else {
      this.removeAttribute('selected');
    }
  }

  constructor() {
    // If you define a constructor, always call super() first!
    // This is specific to CE and required by the spec.
    super();
    /* handle clicks */
    for (const item of this.children) {
      item.addEventListener('click', e => {
        console.log(e);
      });
    }
  }

  connectedCallback() {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        for (var item of JSON.parse(this.responseText)) {
          console.log(item);
          this.addStaff(item);
        }
      }
    };
    xhttp.open('GET', "/api/get_staff");
    xhttp.send();
  }
}
  addStaff() {
  console.log("add staff");
}
customElements.define('staff-list', StaffList);

I tried the keyword this but gives me the XML request object instead of my custom element.

2

Answers


  1. The problem here is caused by this is not bounded to the class instance that you created as you have expected. When you create a function with function() { constructor, it will create a new scope and bound to whoever that called it, in this case since it is called by XMLHttpRequest, it will will be bound to it instead.

    There are 2 solutions in your case, a solution would be setting this into another constant such as self which you can then refer in your function() { in your XMLHTTPRequest.

    connectedCallback() {
      const self = this;
      var xhttp = new XMLHttpRequest();
      xhttp.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
          for (var item of JSON.parse(this.responseText)) {
            console.log(item);
            self.addStaff(item);
          }
        }
      };
      xhttp.open('GET', '/api/get_staff');
      xhttp.send();
    }
    

    Another solution is to use anonymous function which doesn’t depends on the caller. However, you wouldn’t be able to use this to refer to xhttp but rather you need to use xhttp directly.

    connectedCallback() {
      var xhttp = new XMLHttpRequest();
      xhttp.onreadystatechange = () => {
        if (xhttp.readyState == 4 && xhttp.status == 200) {
          for (var item of JSON.parse(xhttp.responseText)) {
            console.log(item);
            this.addStaff(item);
          }
        }
      };
      xhttp.open('GET', '/api/get_staff');
      xhttp.send();
    }
    
    Login or Signup to reply.
  2. I would really recommend switching from using XMLHttpRequest to fetch, but that is not the question.

    And from var to let and there seems to be a closing bracket too much after connectedCallback…?

    But that is none of my business 😉

    Now for the main problem:

    In JavaScript a function creates a new this scope, unless it is an arrow function.

    You are using a traditional / non-arrow function for your onreadystatechange (and you need to do so, because this inside this function is the xhttp object, an instance of XMLHttpRequest. The problem is: You want to access the instance of your class from inside this function as well.

    The traditional trick for this (pun intended) is storing the instance of your class in another variable before going into the the ‘inner’ function, I usually call this variable that (for fun). Keep a lookout for that in the following code:

     connectedCallback() {
        var xhttp = new XMLHttpRequest();
        var that = this;
        xhttp.onreadystatechange = function() {
          if (this.readyState == 4 && this.status == 200) {
            for (var item of JSON.parse(this.responseText)) {
              console.log(item);
              that.addStaff(item);
            }
          }
        };
        xhttp.open('GET', "/api/get_staff");
        xhttp.send();
      }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search