5

I'm new to JavaScript and probably trying to emulate Ruby with this. I use StimulusJS, but I think the question is applicable to JS in general.

My goal is to run a method (on button click) which would fetch and display all subcategories of the category from the button. The method/function would first fetch the data and then manipulate the DOM. I wanted to split these into two methods, but when I call the other method (displaySubcategories) from within the first one (getSubcategories) the value of event changes. Same thing with this in the fetch block - I need to assign it first to self to be be later able to related to object itself.

Is there a better way to do this? Like variables accessible to all methods (instance variables in Ruby)? Or I shouldn't be splitting these into two functions at all (not JS way)?

import {Controller} from "stimulus"

export default class extends Controller {
  static targets = ["categoryId", "categoryLabel", "subcategoryList", "subcategoryHeader"]

  getSubcategories() {
    let category_id = event.target.firstElementChild.value
    let url = "/api/categories/" + category_id + "/subcategories?levels=1"
    let self = this
    let e = event

    fetch(url)
      .then(response => response.json())
      .then(json_response => {
        self.displaySubcategories(json_response, e)
      })
  }

  displaySubcategories(json_response, event) {
        subcategories.forEach(function (subcategory) {
          let category_label_copy = this.cloneLabel(current_label, subcategory)
          current_label.classList.add("chosen")
          subcategory_list.appendChild(category_label_copy)
        }, this)
  }
}


expenses#getSubcategories">


sloneorzeszki
  • 1,274
  • 3
  • 12
  • 22
  • Is `getSubcategories` a method of an object or an independent function? If the former, can you please show all the relevant code? – Armen Vardanyan Apr 19 '19 at 11:39
  • It's a method of an object. There's only the second method (`displaySubcategories`) which is not ready yet, as I stopped to think about how should I structure that. This is a generic question though - if I want to use `event` - do I need to pass it from method to method? Is it available only in the first called method and cannot be assigned to any variable accessible from other methods as well? – sloneorzeszki Apr 19 '19 at 11:45
  • I just want to take a more detailed look into your object. Is it an instance of a class or created with an object literal? – Armen Vardanyan Apr 19 '19 at 11:46
  • 2
    And BTW, as you use arrow functions, you should not experience problems with the `this` keyword, it is not needed to store it in another variable (`self` in your case) – Armen Vardanyan Apr 19 '19 at 11:47
  • Where `event` comes from? It isn't declared anywhere. – mbojko Apr 19 '19 at 11:51
  • I updated the original post. The class is the Stimulus controller. The event comes from clicking the button which then calls the controller's action (`getSubcategories`). – sloneorzeszki Apr 19 '19 at 11:56
  • Actually, `event` is a property of every `Window`, it references the "current" event being handled. Yes, that's right. It's a remnant from older days of Web APIs. It's not terribly useful today nor is it recommended, but [it's a thing](https://developer.mozilla.org/en-US/docs/Web/API/Window/event). The curiosity here would be that this property, as all properties, can be shadowed by a stray or deliberate declaration of a variable/constant named `event` in the same scope, which would be very confusing. My advice is to not use variable name `event` or be prepared for confusion. – Armen Michaeli Apr 19 '19 at 12:02
  • And you should definitely pass things like event references from method to method -- global state is hard to manage, and if you struggle with the kind of problems shown in your question, better stay away from global state as much as you can -- pass your objects to methods as arguments so you can debug your code better and to have stronger guarantees it works (because you know what is passed around to where). And like I said, do not declare variables, including function arguments, called `event` -- these shadow `Window.event` and will do more harm than good, I bet. – Armen Michaeli Apr 19 '19 at 12:09
  • @amn Thanks for the answer (why in the comments though?). I have Ruby background so this is where my question stems from. I don't intend to use global variables, I am looking more for something like instance variables in Ruby, which are available to all class methods but not outside the class itself. But I assume it doesn't exist if it wasn't suggested yet. – sloneorzeszki Apr 19 '19 at 12:15
  • 1
    `this.foobar = 1` -- that's assigning a value to an "instance variable" (when inside a non-static class method), what's the problem? You need to read up on class anatomy in JavaScript. Also, I comment because my comments aren't an answer to your question, as I find your question vague and/or subjective (no definite answer). If you clarify what you're after, in the question, perhaps someone, including me, may provide a worthy answer. – Armen Michaeli Apr 19 '19 at 14:12
  • @amn Sorry didn't expect it to cause such a confusion. `this.foobar` would be fine if not for constantly changing value of `this`. My question is more about having a variable defined once, but no matter where it's read, it will give the same value (meaning, it's not influenced by changing value of `this`). I added a `forEach` loop to my original post - if I tried to read `this.foobar` within the loop, `this` would no longer point to the class (in my case it will, as I passed it manually). – sloneorzeszki Apr 19 '19 at 18:48

0 Answers0